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 { if node.op == .bit_not && !c.pref.translated && !c.file.is_translated {
c.type_error_for_operator('~', 'integer', right_sym.name, node.pos) 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 { 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) 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 left_enum := left_sym.info as ast.Enum
right_enum := right_sym.info as ast.Enum right_enum := right_sym.info as ast.Enum
if left_enum.is_flag && right_enum.is_flag { if left_enum.is_flag && right_enum.is_flag {
// `[flag]` tagged enums are a special case that allow also `|` and `&` binary operators // `@[flag]` tagged enums are a special case that allow also `|` and `&` binary operators
if node.op !in [.pipe, .amp] { 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', c.error('only `==`, `!=`, `|`, `&`, `^` and `~` are defined on `@[flag]` tagged `enum`, use an explicit cast to `int` if needed',
node.pos) node.pos)
} }
} else if !c.pref.translated && !c.file.is_translated && !left_type.has_flag(.generic) } 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 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
7 | 14 |
8 | fn main() { 15 | fn main() {
9 | println(FilePerm.read > FilePerm.write) 16 | println(FilePerm.read > FilePerm.write)
| ^ | ^
10 | println(FilePerm.write + FilePerm.exec) 17 | println(FilePerm.write + FilePerm.exec)
11 | println(FilePerm.write && FilePerm.exec) 18 | 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 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
8 | fn main() { 15 | fn main() {
9 | println(FilePerm.read > FilePerm.write) 16 | println(FilePerm.read > FilePerm.write)
10 | println(FilePerm.write + FilePerm.exec) 17 | println(FilePerm.write + FilePerm.exec)
| ^ | ^
11 | println(FilePerm.write && FilePerm.exec) 18 | println(FilePerm.write && FilePerm.exec)
12 | } 19 |
vlib/v/checker/tests/enum_op_flag_err.vv:11:10: error: left operand for `&&` is not a boolean vlib/v/checker/tests/enum_op_flag_err.vv:18:10: error: left operand for `&&` is not a boolean
9 | println(FilePerm.read > FilePerm.write) 16 | println(FilePerm.read > FilePerm.write)
10 | println(FilePerm.write + FilePerm.exec) 17 | println(FilePerm.write + FilePerm.exec)
11 | println(FilePerm.write && FilePerm.exec) 18 | println(FilePerm.write && FilePerm.exec)
| ~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~
12 | } 19 |
vlib/v/checker/tests/enum_op_flag_err.vv:11:28: error: right operand for `&&` is not a boolean 20 | f := Flags.bit0 | Flags.bit1
9 | println(FilePerm.read > FilePerm.write) vlib/v/checker/tests/enum_op_flag_err.vv:18:28: error: right operand for `&&` is not a boolean
10 | println(FilePerm.write + FilePerm.exec) 16 | println(FilePerm.read > FilePerm.write)
11 | println(FilePerm.write && FilePerm.exec) 17 | println(FilePerm.write + FilePerm.exec)
18 | println(FilePerm.write && FilePerm.exec)
| ~~~~~~~~~~~~~ | ~~~~~~~~~~~~~
12 | } 19 |
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 20 | f := Flags.bit0 | Flags.bit1
9 | println(FilePerm.read > FilePerm.write) 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
10 | println(FilePerm.write + FilePerm.exec) 16 | println(FilePerm.read > FilePerm.write)
11 | println(FilePerm.write && FilePerm.exec) 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 exec
} }
enum Flags {
bit0
bit1
bit2
bit3
}
fn main() { fn main() {
println(FilePerm.read > FilePerm.write) println(FilePerm.read > FilePerm.write)
println(FilePerm.write + FilePerm.exec) println(FilePerm.write + FilePerm.exec)
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 abc2 = Foo.a | .b | .c
const abc3 = Foo.a ^ .b
const abc4 = ~Foo.a
fn test_main() { fn test_main() {
assert dump(a) == Foo.a assert dump(a) == Foo.a
assert dump(ab) == Foo.a | Foo.b assert dump(ab) == Foo.a | Foo.b
assert dump(ab2) == Foo.a | .b assert dump(ab2) == Foo.a | .b
assert dump(abc) == Foo.a | Foo.b | Foo.c assert dump(abc) == Foo.a | Foo.b | Foo.c
assert dump(abc2) == Foo.a | .b | .c assert dump(abc2) == Foo.a | .b | .c
assert dump(abc3) == Foo.a ^ .b
assert dump(abc4) == ~Foo.a
} }