diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index d354d0263d..2aaf3593f4 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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) diff --git a/vlib/v/checker/infix.v b/vlib/v/checker/infix.v index 7f3e4f66c1..85dccb9e22 100644 --- a/vlib/v/checker/infix.v +++ b/vlib/v/checker/infix.v @@ -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) diff --git a/vlib/v/checker/tests/enum_op_flag_err.out b/vlib/v/checker/tests/enum_op_flag_err.out index cf9ce5117a..fe32f38d53 100644 --- a/vlib/v/checker/tests/enum_op_flag_err.out +++ b/vlib/v/checker/tests/enum_op_flag_err.out @@ -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 | } diff --git a/vlib/v/checker/tests/enum_op_flag_err.vv b/vlib/v/checker/tests/enum_op_flag_err.vv index 7489b0de29..ce511d7a2f 100644 --- a/vlib/v/checker/tests/enum_op_flag_err.vv +++ b/vlib/v/checker/tests/enum_op_flag_err.vv @@ -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) } diff --git a/vlib/v/tests/enum_flag_test.v b/vlib/v/tests/enum_flag_test.v index 98a9063a9f..3e01ad3d9e 100644 --- a/vlib/v/tests/enum_flag_test.v +++ b/vlib/v/tests/enum_flag_test.v @@ -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 }