mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: add support for enum Xyz as u64 {
+ tests (#16246)
This commit is contained in:
parent
4564a47fbc
commit
02c3af2432
@ -3237,7 +3237,7 @@ You can see the complete
|
|||||||
### Enums
|
### Enums
|
||||||
|
|
||||||
```v
|
```v
|
||||||
enum Color {
|
enum Color as u8 {
|
||||||
red
|
red
|
||||||
green
|
green
|
||||||
blue
|
blue
|
||||||
@ -3254,6 +3254,8 @@ match color {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The enum type can be any integer type, but can be ommited, if it is `int`: `enum Color {`.
|
||||||
|
|
||||||
Enum match must be exhaustive or have an `else` branch.
|
Enum match must be exhaustive or have an `else` branch.
|
||||||
This ensures that if a new enum field is added, it's handled everywhere in the code.
|
This ensures that if a new enum field is added, it's handled everywhere in the code.
|
||||||
|
|
||||||
|
2
make.bat
2
make.bat
@ -205,7 +205,7 @@ if exist "%InstallDir%\Common7\Tools\vsdevcmd.bat" (
|
|||||||
set ObjFile=.v.c.obj
|
set ObjFile=.v.c.obj
|
||||||
|
|
||||||
echo ^> Attempting to build v_win.c with MSVC
|
echo ^> Attempting to build v_win.c with MSVC
|
||||||
cl.exe /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no
|
cl.exe /volatile:ms /Fo%ObjFile% /W0 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no
|
||||||
if %ERRORLEVEL% NEQ 0 (
|
if %ERRORLEVEL% NEQ 0 (
|
||||||
echo In some cases, compile errors happen because of the MSVC compiler version
|
echo In some cases, compile errors happen because of the MSVC compiler version
|
||||||
cl.exe
|
cl.exe
|
||||||
|
@ -118,7 +118,7 @@ pub enum ImageType {
|
|||||||
_num
|
_num
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CubeFace {
|
pub enum CubeFace as u32 {
|
||||||
pos_x
|
pos_x
|
||||||
neg_x
|
neg_x
|
||||||
pos_y
|
pos_y
|
||||||
|
@ -1164,6 +1164,7 @@ pub:
|
|||||||
comments []Comment // comments before the first EnumField
|
comments []Comment // comments before the first EnumField
|
||||||
fields []EnumField // all the enum fields
|
fields []EnumField // all the enum fields
|
||||||
attrs []Attr // attributes of enum declaration
|
attrs []Attr // attributes of enum declaration
|
||||||
|
typ Type // the default is `int`; can be changed by `enum Big as u64 { a = 5 }`
|
||||||
pos token.Pos
|
pos token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1401,7 +1401,9 @@ pub fn (mut c Checker) const_decl(mut node ast.ConstDecl) {
|
|||||||
|
|
||||||
pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
|
pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
|
||||||
c.check_valid_pascal_case(node.name, 'enum name', node.pos)
|
c.check_valid_pascal_case(node.name, 'enum name', node.pos)
|
||||||
mut seen := []i64{cap: node.fields.len}
|
mut useen := []u64{}
|
||||||
|
mut iseen := []i64{}
|
||||||
|
mut seen_enum_field_names := map[string]int{}
|
||||||
if node.fields.len == 0 {
|
if node.fields.len == 0 {
|
||||||
c.error('enum cannot be empty', node.pos)
|
c.error('enum cannot be empty', node.pos)
|
||||||
}
|
}
|
||||||
@ -1410,35 +1412,114 @@ pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
|
|||||||
c.error('`builtin` module cannot have enums', node.pos)
|
c.error('`builtin` module cannot have enums', node.pos)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
mut enum_imin := i64(0)
|
||||||
|
mut enum_imax := i64(0)
|
||||||
|
mut enum_umin := u64(0)
|
||||||
|
mut enum_umax := u64(0)
|
||||||
|
mut signed := true
|
||||||
|
senum_type := c.table.type_to_str(node.typ)
|
||||||
|
match node.typ {
|
||||||
|
ast.i8_type {
|
||||||
|
signed, enum_imin, enum_imax = true, -128, 0x7F
|
||||||
|
}
|
||||||
|
ast.i16_type {
|
||||||
|
signed, enum_imin, enum_imax = true, -32_768, 0x7FFF
|
||||||
|
}
|
||||||
|
ast.int_type {
|
||||||
|
signed, enum_imin, enum_imax = true, -2_147_483_648, 0x7FFF_FFFF
|
||||||
|
}
|
||||||
|
ast.i64_type {
|
||||||
|
signed, enum_imin, enum_imax = true, i64(-9223372036854775807 - 1), i64(0x7FFF_FFFF_FFFF_FFFF)
|
||||||
|
}
|
||||||
|
//
|
||||||
|
ast.u8_type {
|
||||||
|
signed, enum_umin, enum_umax = false, 0, 0xFF
|
||||||
|
}
|
||||||
|
ast.u16_type {
|
||||||
|
signed, enum_umin, enum_umax = false, 0, 0xFFFF
|
||||||
|
}
|
||||||
|
ast.u32_type {
|
||||||
|
signed, enum_umin, enum_umax = false, 0, 0xFFFF_FFFF
|
||||||
|
}
|
||||||
|
ast.u64_type {
|
||||||
|
signed, enum_umin, enum_umax = false, 0, 0xFFFF_FFFF_FFFF_FFFF
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c.error('`$senum_type` is not one of `i8`,`i16`,`int`,`i64`,`u8`,`u16`,`u32`,`u64`',
|
||||||
|
node.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if enum_imin > 0 {
|
||||||
|
// ensure that the minimum value is negative, even with msvc, which has a bug that makes -2147483648 positive ...
|
||||||
|
enum_imin *= -1
|
||||||
|
}
|
||||||
for i, mut field in node.fields {
|
for i, mut field in node.fields {
|
||||||
if !c.pref.experimental && util.contains_capital(field.name) {
|
if !c.pref.experimental && util.contains_capital(field.name) {
|
||||||
// TODO C2V uses hundreds of enums with capitals, remove -experimental check once it's handled
|
// TODO C2V uses hundreds of enums with capitals, remove -experimental check once it's handled
|
||||||
c.error('field name `$field.name` cannot contain uppercase letters, use snake_case instead',
|
c.error('field name `$field.name` cannot contain uppercase letters, use snake_case instead',
|
||||||
field.pos)
|
field.pos)
|
||||||
}
|
}
|
||||||
for j in 0 .. i {
|
if _ := seen_enum_field_names[field.name] {
|
||||||
if field.name == node.fields[j].name {
|
c.error('duplicate enum field name `$field.name`', field.pos)
|
||||||
c.error('field name `$field.name` duplicate', field.pos)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
seen_enum_field_names[field.name] = i
|
||||||
if field.has_expr {
|
if field.has_expr {
|
||||||
match mut field.expr {
|
match mut field.expr {
|
||||||
ast.IntegerLiteral {
|
ast.IntegerLiteral {
|
||||||
val := field.expr.val.i64()
|
mut overflows := false
|
||||||
if val < checker.int_min || val > checker.int_max {
|
mut uval := u64(0)
|
||||||
c.error('enum value `$val` overflows int', field.expr.pos)
|
mut ival := i64(0)
|
||||||
} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
|
if signed {
|
||||||
&& i64(val) in seen {
|
val := field.expr.val.i64()
|
||||||
c.error('enum value `$val` already exists', field.expr.pos)
|
ival = val
|
||||||
|
if val < enum_imin || val >= enum_imax {
|
||||||
|
c.error('enum value `$field.expr.val` overflows the enum type `$senum_type`, values of which have to be in [$enum_imin, $enum_imax]',
|
||||||
|
field.expr.pos)
|
||||||
|
overflows = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val := field.expr.val.u64()
|
||||||
|
uval = val
|
||||||
|
if val >= enum_umax {
|
||||||
|
c.error('enum value `$field.expr.val` overflows the enum type `$senum_type`, values of which have to be in [$enum_umin, $enum_umax]',
|
||||||
|
field.expr.pos)
|
||||||
|
overflows = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !overflows && !c.pref.translated && !c.file.is_translated
|
||||||
|
&& !node.is_multi_allowed {
|
||||||
|
if (signed && ival in iseen) || (!signed && uval in useen) {
|
||||||
|
c.error('enum value `$field.expr.val` already exists', field.expr.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if signed {
|
||||||
|
iseen << ival
|
||||||
|
} else {
|
||||||
|
useen << uval
|
||||||
}
|
}
|
||||||
seen << i64(val)
|
|
||||||
}
|
}
|
||||||
ast.PrefixExpr {}
|
ast.PrefixExpr {
|
||||||
|
dump(field.expr)
|
||||||
|
}
|
||||||
ast.InfixExpr {
|
ast.InfixExpr {
|
||||||
// Handle `enum Foo { x = 1 + 2 }`
|
// Handle `enum Foo { x = 1 + 2 }`
|
||||||
c.infix_expr(mut field.expr)
|
c.infix_expr(mut field.expr)
|
||||||
}
|
}
|
||||||
// ast.ParExpr {} // TODO allow `.x = (1+2)`
|
ast.ParExpr {
|
||||||
|
c.expr(field.expr)
|
||||||
|
}
|
||||||
|
ast.CastExpr {
|
||||||
|
fe_type := c.cast_expr(mut field.expr)
|
||||||
|
if node.typ != fe_type {
|
||||||
|
sfe_type := c.table.type_to_str(fe_type)
|
||||||
|
c.error('the type of the enum value `$sfe_type` != the enum type itself `$senum_type`',
|
||||||
|
field.expr.pos)
|
||||||
|
}
|
||||||
|
if !fe_type.is_pure_int() {
|
||||||
|
c.error('the type of an enum value must be an integer type, like i8, u8, int, u64 etc.',
|
||||||
|
field.expr.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if mut field.expr is ast.Ident {
|
if mut field.expr is ast.Ident {
|
||||||
if field.expr.language == .c {
|
if field.expr.language == .c {
|
||||||
@ -1449,21 +1530,38 @@ pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
|
|||||||
if pos.pos == 0 {
|
if pos.pos == 0 {
|
||||||
pos = field.pos
|
pos = field.pos
|
||||||
}
|
}
|
||||||
c.error('default value for enum has to be an integer', pos)
|
c.error('the default value for an enum has to be an integer', pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if seen.len > 0 {
|
if signed {
|
||||||
last := seen[seen.len - 1]
|
if iseen.len > 0 {
|
||||||
if last == checker.int_max {
|
ilast := iseen[iseen.len - 1]
|
||||||
c.error('enum value overflows', field.pos)
|
if ilast == enum_imax {
|
||||||
} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
|
c.error('enum value overflows type `$senum_type`, which has a maximum value of $enum_imax',
|
||||||
&& last + 1 in seen {
|
field.pos)
|
||||||
c.error('enum value `${last + 1}` already exists', field.pos)
|
} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
|
||||||
|
&& ilast + 1 in iseen {
|
||||||
|
c.error('enum value `${ilast + 1}` already exists', field.pos)
|
||||||
|
}
|
||||||
|
iseen << ilast + 1
|
||||||
|
} else {
|
||||||
|
iseen << 0
|
||||||
}
|
}
|
||||||
seen << last + 1
|
|
||||||
} else {
|
} else {
|
||||||
seen << 0
|
if useen.len > 0 {
|
||||||
|
ulast := useen[useen.len - 1]
|
||||||
|
if ulast == enum_umax {
|
||||||
|
c.error('enum value overflows type `$senum_type`, which has a maximum value of $enum_umax',
|
||||||
|
field.pos)
|
||||||
|
} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
|
||||||
|
&& ulast + 1 in useen {
|
||||||
|
c.error('enum value `${ulast + 1}` already exists', field.pos)
|
||||||
|
}
|
||||||
|
useen << ulast + 1
|
||||||
|
} else {
|
||||||
|
useen << 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2637,21 +2735,25 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
|||||||
if to_sym.kind == .enum_ {
|
if to_sym.kind == .enum_ {
|
||||||
if mut node.expr is ast.IntegerLiteral {
|
if mut node.expr is ast.IntegerLiteral {
|
||||||
enum_typ_name := c.table.get_type_name(to_type)
|
enum_typ_name := c.table.get_type_name(to_type)
|
||||||
node_val := node.expr.val.int()
|
node_val := node.expr.val.i64()
|
||||||
|
|
||||||
if enum_decl := c.table.enum_decls[to_sym.name] {
|
if enum_decl := c.table.enum_decls[to_sym.name] {
|
||||||
mut in_range := false
|
mut in_range := false
|
||||||
if enum_decl.is_flag {
|
if enum_decl.is_flag {
|
||||||
// if a flag enum has 4 variants, the maximum possible value would have all 4 flags set (0b1111)
|
// if a flag enum has 4 variants, the maximum possible value would have all 4 flags set (0b1111)
|
||||||
max_val := (1 << enum_decl.fields.len) - 1
|
max_val := (u64(1) << enum_decl.fields.len) - 1
|
||||||
in_range = node_val >= 0 && node_val <= max_val
|
in_range = node_val >= 0 && u64(node_val) <= max_val
|
||||||
} else {
|
} else {
|
||||||
mut enum_val := 0
|
mut enum_val := i64(0)
|
||||||
|
|
||||||
for enum_field in enum_decl.fields {
|
for enum_field in enum_decl.fields {
|
||||||
// check if the field of the enum value is an integer literal
|
// check if the field of the enum value is an integer literal
|
||||||
if enum_field.expr is ast.IntegerLiteral {
|
if enum_field.expr is ast.IntegerLiteral {
|
||||||
enum_val = enum_field.expr.val.int()
|
enum_val = enum_field.expr.val.i64()
|
||||||
|
} else if comptime_value := c.eval_comptime_const_expr(enum_field.expr,
|
||||||
|
0)
|
||||||
|
{
|
||||||
|
enum_val = comptime_value.i64() or { -1 }
|
||||||
}
|
}
|
||||||
if node_val == enum_val {
|
if node_val == enum_val {
|
||||||
in_range = true
|
in_range = true
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
vlib/v/checker/tests/enum_err.vv:4:13: error: default value for enum has to be an integer
|
vlib/v/checker/tests/enum_err.vv:4:13: error: the default value for an enum has to be an integer
|
||||||
2 |
|
2 |
|
||||||
3 | enum Color {
|
3 | enum Color {
|
||||||
4 | green = 'green'
|
4 | green = 'green'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
vlib/v/checker/tests/enum_field_name_duplicate_err.vv:5:2: error: field name `green` duplicate
|
vlib/v/checker/tests/enum_field_name_duplicate_err.vv:5:2: error: duplicate enum field name `green`
|
||||||
3 | yellow
|
3 | yellow
|
||||||
4 | blue
|
4 | blue
|
||||||
5 | green
|
5 | green
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
vlib/v/checker/tests/enum_field_overflow.vv:4:2: error: enum value overflows
|
vlib/v/checker/tests/enum_field_overflow.vv:3:10: error: enum value `2147483647` overflows the enum type `int`, values of which have to be in [-2147483648, 2147483647]
|
||||||
|
1 | enum Color {
|
||||||
|
2 | red
|
||||||
|
3 | green = 2147483647
|
||||||
|
| ~~~~~~~~~~
|
||||||
|
4 | blue
|
||||||
|
5 | }
|
||||||
|
vlib/v/checker/tests/enum_field_overflow.vv:4:2: error: enum value overflows type `int`, which has a maximum value of 2147483647
|
||||||
2 | red
|
2 | red
|
||||||
3 | green = 2147483647
|
3 | green = 2147483647
|
||||||
4 | blue
|
4 | blue
|
||||||
|
@ -1,7 +1,90 @@
|
|||||||
vlib/v/checker/tests/enum_field_value_overflow.vv:3:10: error: enum value `2147483648` overflows int
|
vlib/v/checker/tests/enum_field_value_overflow.vv:3:10: error: enum value `2147483648` overflows the enum type `int`, values of which have to be in [-2147483648, 2147483647]
|
||||||
1 | enum Color {
|
1 | enum Color {
|
||||||
2 | red
|
2 | red
|
||||||
3 | green = 2147483648
|
3 | green = 2147483648
|
||||||
| ~~~~~~~~~~
|
| ~~~~~~~~~~
|
||||||
4 | blue
|
4 | blue
|
||||||
5 | }
|
5 | }
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:7:1: error: `string` is not one of `i8`,`i16`,`int`,`i64`,`u8`,`u16`,`u32`,`u64`
|
||||||
|
5 | }
|
||||||
|
6 |
|
||||||
|
7 | enum ColorString as string {
|
||||||
|
| ~~~~~~~~~~~~~~~~
|
||||||
|
8 | red
|
||||||
|
9 | green = 'abc'
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:9:10: error: the default value for an enum has to be an integer
|
||||||
|
7 | enum ColorString as string {
|
||||||
|
8 | red
|
||||||
|
9 | green = 'abc'
|
||||||
|
| ~~~~~
|
||||||
|
10 | blue
|
||||||
|
11 | }
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:10:2: error: enum value overflows type `string`, which has a maximum value of 0
|
||||||
|
8 | red
|
||||||
|
9 | green = 'abc'
|
||||||
|
10 | blue
|
||||||
|
| ~~~~
|
||||||
|
11 | }
|
||||||
|
12 |
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:15:10: error: enum value `128` overflows the enum type `i8`, values of which have to be in [-128, 127]
|
||||||
|
13 | enum ColorI8 as i8 {
|
||||||
|
14 | red
|
||||||
|
15 | green = 128
|
||||||
|
| ~~~
|
||||||
|
16 | blue
|
||||||
|
17 | }
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:21:10: error: enum value `32769` overflows the enum type `i16`, values of which have to be in [-32768, 32767]
|
||||||
|
19 | enum ColorI16 as i16 {
|
||||||
|
20 | red
|
||||||
|
21 | green = 32769
|
||||||
|
| ~~~~~
|
||||||
|
22 | blue
|
||||||
|
23 | }
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:27:10: error: enum value `2147483648` overflows the enum type `int`, values of which have to be in [-2147483648, 2147483647]
|
||||||
|
25 | enum ColorI32 {
|
||||||
|
26 | red
|
||||||
|
27 | green = 2147483648
|
||||||
|
| ~~~~~~~~~~
|
||||||
|
28 | blue
|
||||||
|
29 | }
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:33:10: error: enum value `9223372036854775808` overflows the enum type `i64`, values of which have to be in [-9223372036854775808, 9223372036854775807]
|
||||||
|
31 | enum ColorI64 as i64 {
|
||||||
|
32 | red
|
||||||
|
33 | green = 9223372036854775808
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~
|
||||||
|
34 | blue
|
||||||
|
35 | }
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:34:2: error: enum value overflows type `i64`, which has a maximum value of 9223372036854775807
|
||||||
|
32 | red
|
||||||
|
33 | green = 9223372036854775808
|
||||||
|
34 | blue
|
||||||
|
| ~~~~
|
||||||
|
35 | }
|
||||||
|
36 |
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:56:10: error: enum value `256` overflows the enum type `u8`, values of which have to be in [0, 255]
|
||||||
|
54 | // These should work however, since the type is unsigned:
|
||||||
|
55 | enum ColorU8ShouldFail as u8 {
|
||||||
|
56 | green = 256
|
||||||
|
| ~~~
|
||||||
|
57 | }
|
||||||
|
58 |
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:60:10: error: enum value `65536` overflows the enum type `u16`, values of which have to be in [0, 65535]
|
||||||
|
58 |
|
||||||
|
59 | enum ColorU16ShouldFail as u16 {
|
||||||
|
60 | green = 65536
|
||||||
|
| ~~~~~
|
||||||
|
61 | }
|
||||||
|
62 |
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:64:10: error: enum value `4294967296` overflows the enum type `u32`, values of which have to be in [0, 4294967295]
|
||||||
|
62 |
|
||||||
|
63 | enum ColorU32ShouldFail as u32 {
|
||||||
|
64 | green = 4294967296
|
||||||
|
| ~~~~~~~~~~
|
||||||
|
65 | }
|
||||||
|
66 |
|
||||||
|
vlib/v/checker/tests/enum_field_value_overflow.vv:68:10: error: enum value `18446744073709551616` overflows the enum type `u64`, values of which have to be in [0, 18446744073709551615]
|
||||||
|
66 |
|
||||||
|
67 | enum ColorU64ShouldFail as u64 {
|
||||||
|
68 | green = 18446744073709551616
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~
|
||||||
|
69 | }
|
||||||
|
@ -3,3 +3,67 @@ enum Color {
|
|||||||
green = 2147483648
|
green = 2147483648
|
||||||
blue
|
blue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ColorString as string {
|
||||||
|
red
|
||||||
|
green = 'abc'
|
||||||
|
blue
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorI8 as i8 {
|
||||||
|
red
|
||||||
|
green = 128
|
||||||
|
blue
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorI16 as i16 {
|
||||||
|
red
|
||||||
|
green = 32769
|
||||||
|
blue
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorI32 {
|
||||||
|
red
|
||||||
|
green = 2147483648
|
||||||
|
blue
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorI64 as i64 {
|
||||||
|
red
|
||||||
|
green = 9223372036854775808
|
||||||
|
blue
|
||||||
|
}
|
||||||
|
|
||||||
|
// These should work however, since the type is unsigned:
|
||||||
|
enum ColorU8ShouldWork as u8 {
|
||||||
|
green = 128
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorU16ShouldWork as u16 {
|
||||||
|
green = 32769
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorU32ShouldWork as u32 {
|
||||||
|
green = 2147483648
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorU64ShouldWork as u64 {
|
||||||
|
green = 9223372036854775808
|
||||||
|
}
|
||||||
|
|
||||||
|
// These should work however, since the type is unsigned:
|
||||||
|
enum ColorU8ShouldFail as u8 {
|
||||||
|
green = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorU16ShouldFail as u16 {
|
||||||
|
green = 65536
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorU32ShouldFail as u32 {
|
||||||
|
green = 4294967296
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ColorU64ShouldFail as u64 {
|
||||||
|
green = 18446744073709551616
|
||||||
|
}
|
||||||
|
@ -938,7 +938,11 @@ pub fn (mut f Fmt) enum_decl(node ast.EnumDecl) {
|
|||||||
if node.is_pub {
|
if node.is_pub {
|
||||||
f.write('pub ')
|
f.write('pub ')
|
||||||
}
|
}
|
||||||
name := node.name.after('.')
|
mut name := node.name.after('.')
|
||||||
|
if node.typ != ast.int_type {
|
||||||
|
senum_type := f.table.type_to_str_using_aliases(node.typ, f.mod2alias)
|
||||||
|
name += ' as $senum_type'
|
||||||
|
}
|
||||||
if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line {
|
if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line {
|
||||||
f.writeln('enum $name {}\n')
|
f.writeln('enum $name {}\n')
|
||||||
return
|
return
|
||||||
|
@ -306,8 +306,10 @@ fn (mut g Gen) gen_str_for_enum(info ast.Enum, styp string, str_fn_name string)
|
|||||||
clean_name := util.strip_main_name(styp.replace('__', '.'))
|
clean_name := util.strip_main_name(styp.replace('__', '.'))
|
||||||
g.auto_str_funcs.writeln('\tstring ret = _SLIT("$clean_name{");')
|
g.auto_str_funcs.writeln('\tstring ret = _SLIT("$clean_name{");')
|
||||||
g.auto_str_funcs.writeln('\tint first = 1;')
|
g.auto_str_funcs.writeln('\tint first = 1;')
|
||||||
|
g.auto_str_funcs.writeln('\tu64 zit = (u64)it;')
|
||||||
for i, val in info.vals {
|
for i, val in info.vals {
|
||||||
g.auto_str_funcs.writeln('\tif (it & (1 << $i)) { if (!first) { ret = string__plus(ret, _SLIT(" | "));} ret = string__plus(ret, _SLIT(".$val")); first = 0;}')
|
mask := u64(1) << i
|
||||||
|
g.auto_str_funcs.writeln('\tif (zit & 0x${mask:016x}U) { if (!first) { ret = string__plus(ret, _SLIT(" | "));} ret = string__plus(ret, _SLIT(".$val")); first = 0;}')
|
||||||
}
|
}
|
||||||
g.auto_str_funcs.writeln('\tret = string__plus(ret, _SLIT("}"));')
|
g.auto_str_funcs.writeln('\tret = string__plus(ret, _SLIT("}"));')
|
||||||
g.auto_str_funcs.writeln('\treturn ret;')
|
g.auto_str_funcs.writeln('\treturn ret;')
|
||||||
|
@ -3677,6 +3677,33 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
|||||||
fn (mut g Gen) enum_decl(node ast.EnumDecl) {
|
fn (mut g Gen) enum_decl(node ast.EnumDecl) {
|
||||||
enum_name := util.no_dots(node.name)
|
enum_name := util.no_dots(node.name)
|
||||||
is_flag := node.is_flag
|
is_flag := node.is_flag
|
||||||
|
if g.pref.ccompiler == 'msvc' {
|
||||||
|
mut last_value := '0'
|
||||||
|
enum_typ_name := g.table.get_type_name(node.typ)
|
||||||
|
g.enum_typedefs.writeln('typedef $enum_typ_name $enum_name;')
|
||||||
|
for i, field in node.fields {
|
||||||
|
g.enum_typedefs.write_string('\t#define ${enum_name}__$field.name ')
|
||||||
|
g.enum_typedefs.write_string('(')
|
||||||
|
if is_flag {
|
||||||
|
g.enum_typedefs.write_string((u64(1) << i).str())
|
||||||
|
g.enum_typedefs.write_string('ULL')
|
||||||
|
} else if field.has_expr {
|
||||||
|
expr_str := g.expr_string(field.expr)
|
||||||
|
g.enum_typedefs.write_string(expr_str)
|
||||||
|
last_value = expr_str
|
||||||
|
} else {
|
||||||
|
if i != 0 {
|
||||||
|
last_value += '+1'
|
||||||
|
}
|
||||||
|
g.enum_typedefs.write_string(last_value)
|
||||||
|
}
|
||||||
|
g.enum_typedefs.writeln(')')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if node.typ != ast.int_type {
|
||||||
|
g.enum_typedefs.writeln('#pragma pack(push, 1)')
|
||||||
|
}
|
||||||
g.enum_typedefs.writeln('typedef enum {')
|
g.enum_typedefs.writeln('typedef enum {')
|
||||||
mut cur_enum_expr := ''
|
mut cur_enum_expr := ''
|
||||||
mut cur_enum_offset := 0
|
mut cur_enum_offset := 0
|
||||||
@ -3690,8 +3717,9 @@ fn (mut g Gen) enum_decl(node ast.EnumDecl) {
|
|||||||
cur_enum_offset = 0
|
cur_enum_offset = 0
|
||||||
} else if is_flag {
|
} else if is_flag {
|
||||||
g.enum_typedefs.write_string(' = ')
|
g.enum_typedefs.write_string(' = ')
|
||||||
cur_enum_expr = '1 << $i'
|
cur_enum_expr = 'u64(1) << $i'
|
||||||
g.enum_typedefs.write_string((1 << i).str())
|
g.enum_typedefs.write_string((u64(1) << i).str())
|
||||||
|
g.enum_typedefs.write_string('U')
|
||||||
cur_enum_offset = 0
|
cur_enum_offset = 0
|
||||||
}
|
}
|
||||||
cur_value := if cur_enum_offset > 0 {
|
cur_value := if cur_enum_offset > 0 {
|
||||||
@ -3702,7 +3730,11 @@ fn (mut g Gen) enum_decl(node ast.EnumDecl) {
|
|||||||
g.enum_typedefs.writeln(', // $cur_value')
|
g.enum_typedefs.writeln(', // $cur_value')
|
||||||
cur_enum_offset++
|
cur_enum_offset++
|
||||||
}
|
}
|
||||||
g.enum_typedefs.writeln('} $enum_name;\n')
|
packed_attribute := if node.typ != ast.int_type { '__attribute__((packed))' } else { '' }
|
||||||
|
g.enum_typedefs.writeln('} $packed_attribute $enum_name;')
|
||||||
|
if node.typ != ast.int_type {
|
||||||
|
g.enum_typedefs.writeln('#pragma pack(pop)\n')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) enum_expr(node ast.Expr) {
|
fn (mut g Gen) enum_expr(node ast.Expr) {
|
||||||
|
@ -31,7 +31,7 @@ fn match_test() {
|
|||||||
assert false
|
assert false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// println(color)
|
// println(color)
|
||||||
assert num == 3
|
assert num == 3
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ fn (_ Bar) baz() {}
|
|||||||
|
|
||||||
fn test_enum_variant_and_method_name_clash() {
|
fn test_enum_variant_and_method_name_clash() {
|
||||||
x := Bar.baz
|
x := Bar.baz
|
||||||
// println(x)
|
// println(x)
|
||||||
x.baz()
|
x.baz()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,4 +73,5 @@ fn main() {
|
|||||||
match_test()
|
match_test()
|
||||||
test_enum_variant_and_method_name_clash()
|
test_enum_variant_and_method_name_clash()
|
||||||
value_test()
|
value_test()
|
||||||
|
println('OK')
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
OK
|
@ -3665,8 +3665,14 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||||||
return ast.EnumDecl{}
|
return ast.EnumDecl{}
|
||||||
}
|
}
|
||||||
name := p.prepend_mod(enum_name)
|
name := p.prepend_mod(enum_name)
|
||||||
|
mut enum_type := ast.int_type
|
||||||
|
if p.tok.kind == .key_as {
|
||||||
|
p.next()
|
||||||
|
enum_type = p.parse_type()
|
||||||
|
}
|
||||||
p.check(.lcbr)
|
p.check(.lcbr)
|
||||||
enum_decl_comments := p.eat_comments()
|
enum_decl_comments := p.eat_comments()
|
||||||
|
senum_type := p.table.get_type_name(enum_type)
|
||||||
mut vals := []string{}
|
mut vals := []string{}
|
||||||
// mut default_exprs := []ast.Expr{}
|
// mut default_exprs := []ast.Expr{}
|
||||||
mut fields := []ast.EnumField{}
|
mut fields := []ast.EnumField{}
|
||||||
@ -3698,8 +3704,8 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||||||
is_flag := p.attrs.contains('flag')
|
is_flag := p.attrs.contains('flag')
|
||||||
is_multi_allowed := p.attrs.contains('_allow_multiple_values')
|
is_multi_allowed := p.attrs.contains('_allow_multiple_values')
|
||||||
if is_flag {
|
if is_flag {
|
||||||
if fields.len > 32 {
|
if fields.len > 64 {
|
||||||
p.error('when an enum is used as bit field, it must have a max of 32 fields')
|
p.error('when an enum is used as bit field, it must have a max of 64 fields')
|
||||||
return ast.EnumDecl{}
|
return ast.EnumDecl{}
|
||||||
}
|
}
|
||||||
for f in fields {
|
for f in fields {
|
||||||
@ -3712,12 +3718,12 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||||||
pubfn := if p.mod == 'main' { 'fn' } else { 'pub fn' }
|
pubfn := if p.mod == 'main' { 'fn' } else { 'pub fn' }
|
||||||
p.codegen('
|
p.codegen('
|
||||||
//
|
//
|
||||||
[inline] $pubfn ( e &$enum_name) is_empty() bool { return int(*e) == 0 }
|
[inline] $pubfn ( e &$enum_name) is_empty() bool { return ${senum_type}(*e) == 0 }
|
||||||
[inline] $pubfn ( e &$enum_name) has(flag $enum_name) bool { return (int(*e) & (int(flag))) != 0 }
|
[inline] $pubfn ( e &$enum_name) has(flag $enum_name) bool { return (${senum_type}(*e) & (${senum_type}(flag))) != 0 }
|
||||||
[inline] $pubfn ( e &$enum_name) all(flag $enum_name) bool { return (int(*e) & (int(flag))) == int(flag) }
|
[inline] $pubfn ( e &$enum_name) all(flag $enum_name) bool { return (${senum_type}(*e) & (${senum_type}(flag))) == ${senum_type}(flag) }
|
||||||
[inline] $pubfn (mut e $enum_name) set(flag $enum_name) { unsafe{ *e = ${enum_name}(int(*e) | (int(flag))) } }
|
[inline] $pubfn (mut e $enum_name) set(flag $enum_name) { unsafe{ *e = ${enum_name}(${senum_type}(*e) | (${senum_type}(flag))) } }
|
||||||
[inline] $pubfn (mut e $enum_name) clear(flag $enum_name) { unsafe{ *e = ${enum_name}(int(*e) & ~(int(flag))) } }
|
[inline] $pubfn (mut e $enum_name) clear(flag $enum_name) { unsafe{ *e = ${enum_name}(${senum_type}(*e) & ~(${senum_type}(flag))) } }
|
||||||
[inline] $pubfn (mut e $enum_name) toggle(flag $enum_name) { unsafe{ *e = ${enum_name}(int(*e) ^ (int(flag))) } }
|
[inline] $pubfn (mut e $enum_name) toggle(flag $enum_name) { unsafe{ *e = ${enum_name}(${senum_type}(*e) ^ (${senum_type}(flag))) } }
|
||||||
//
|
//
|
||||||
')
|
')
|
||||||
}
|
}
|
||||||
@ -3741,6 +3747,7 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
|||||||
|
|
||||||
enum_decl := ast.EnumDecl{
|
enum_decl := ast.EnumDecl{
|
||||||
name: name
|
name: name
|
||||||
|
typ: enum_type
|
||||||
is_pub: is_pub
|
is_pub: is_pub
|
||||||
is_flag: is_flag
|
is_flag: is_flag
|
||||||
is_multi_allowed: is_multi_allowed
|
is_multi_allowed: is_multi_allowed
|
||||||
|
124
vlib/v/tests/enum_bitfield_64bit_test.v
Normal file
124
vlib/v/tests/enum_bitfield_64bit_test.v
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// TODO: fix the formatting here, when VFMT ON/OFF is working
|
||||||
|
|
||||||
|
[flag]
|
||||||
|
enum PawnsBoard as u64 {
|
||||||
|
a8
|
||||||
|
b8
|
||||||
|
c8
|
||||||
|
d8
|
||||||
|
e8
|
||||||
|
f8
|
||||||
|
g8
|
||||||
|
h8
|
||||||
|
a7
|
||||||
|
b7
|
||||||
|
c7
|
||||||
|
d7
|
||||||
|
e7
|
||||||
|
f7
|
||||||
|
g7
|
||||||
|
h7
|
||||||
|
a6
|
||||||
|
b6
|
||||||
|
c6
|
||||||
|
d6
|
||||||
|
e6
|
||||||
|
f6
|
||||||
|
g6
|
||||||
|
h6
|
||||||
|
a5
|
||||||
|
b5
|
||||||
|
c5
|
||||||
|
d5
|
||||||
|
e5
|
||||||
|
f5
|
||||||
|
g5
|
||||||
|
h5
|
||||||
|
a4
|
||||||
|
b4
|
||||||
|
c4
|
||||||
|
d4
|
||||||
|
e4
|
||||||
|
f4
|
||||||
|
g4
|
||||||
|
h4
|
||||||
|
a3
|
||||||
|
b3
|
||||||
|
c3
|
||||||
|
d3
|
||||||
|
e3
|
||||||
|
f3
|
||||||
|
g3
|
||||||
|
h3
|
||||||
|
a2
|
||||||
|
b2
|
||||||
|
c2
|
||||||
|
d2
|
||||||
|
e2
|
||||||
|
f2
|
||||||
|
g2
|
||||||
|
h2
|
||||||
|
a1
|
||||||
|
b1
|
||||||
|
c1
|
||||||
|
d1
|
||||||
|
e1
|
||||||
|
f1
|
||||||
|
g1
|
||||||
|
h1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_flag_enum_with_64_value_bits() {
|
||||||
|
last_value := PawnsBoard.h1
|
||||||
|
dump(u64(last_value))
|
||||||
|
assert u64(last_value) == 0x8000_0000_0000_0000
|
||||||
|
|
||||||
|
wb := PawnsBoard.a2 | .b2 | .c2 | .d2 | .e2 | .f2 | .g2 | .h2
|
||||||
|
dump(wb)
|
||||||
|
dump(u64(wb))
|
||||||
|
assert u64(wb) == 71776119061217280
|
||||||
|
assert '${u64(wb):064b}' == '0000000011111111000000000000000000000000000000000000000000000000'
|
||||||
|
assert '$wb' == 'PawnsBoard{.a2 | .b2 | .c2 | .d2 | .e2 | .f2 | .g2 | .h2}'
|
||||||
|
|
||||||
|
bb := PawnsBoard.a7 | .b7 | .c7 | .d7 | .e7 | .f7 | .g7 | .h7
|
||||||
|
dump(bb)
|
||||||
|
dump(u64(bb))
|
||||||
|
assert u64(bb) == 65280
|
||||||
|
assert '${u64(bb):064b}' == '0000000000000000000000000000000000000000000000001111111100000000'
|
||||||
|
assert '$bb' == 'PawnsBoard{.a7 | .b7 | .c7 | .d7 | .e7 | .f7 | .g7 | .h7}'
|
||||||
|
|
||||||
|
if false {
|
||||||
|
eprintln('----------------------------------------------')
|
||||||
|
for x in [PawnsBoard.a1, .a2, .a3, .a4, .a5, .a6, .a7, .a8] {
|
||||||
|
eprintln('>> x: $x | hex value: ${u64(x).hex()}')
|
||||||
|
}
|
||||||
|
eprintln('----------------------------------------------')
|
||||||
|
for x in [PawnsBoard.b1, .b2, .b3, .b4, .b5, .b6, .b7, .b8] {
|
||||||
|
eprintln('>> x: $x | hex value: ${u64(x).hex()}')
|
||||||
|
}
|
||||||
|
eprintln('----------------------------------------------')
|
||||||
|
for x in [PawnsBoard.c1, .c2, .c3, .c4, .c5, .c6, .c7, .c8] {
|
||||||
|
eprintln('>> x: $x | hex value: ${u64(x).hex()}')
|
||||||
|
}
|
||||||
|
eprintln('----------------------------------------------')
|
||||||
|
for x in [PawnsBoard.d1, .d2, .d3, .d4, .d5, .d6, .d7, .d8] {
|
||||||
|
eprintln('>> x: $x | hex value: ${u64(x).hex()}')
|
||||||
|
}
|
||||||
|
eprintln('----------------------------------------------')
|
||||||
|
for x in [PawnsBoard.e1, .e2, .e3, .e4, .e5, .e6, .e7, .e8] {
|
||||||
|
eprintln('>> x: $x | hex value: ${u64(x).hex()}')
|
||||||
|
}
|
||||||
|
eprintln('----------------------------------------------')
|
||||||
|
for x in [PawnsBoard.f1, .f2, .f3, .f4, .f5, .f6, .f7, .f8] {
|
||||||
|
eprintln('>> x: $x | hex value: ${u64(x).hex()}')
|
||||||
|
}
|
||||||
|
eprintln('----------------------------------------------')
|
||||||
|
for x in [PawnsBoard.g1, .g2, .g3, .g4, .g5, .g6, .g7, .g8] {
|
||||||
|
eprintln('>> x: $x | hex value: ${u64(x).hex()}')
|
||||||
|
}
|
||||||
|
eprintln('----------------------------------------------')
|
||||||
|
for x in [PawnsBoard.h1, .h2, .h3, .h4, .h5, .h6, .h7, .h8] {
|
||||||
|
eprintln('>> x: $x | hex value: ${u64(x).hex()}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
vlib/v/tests/enum_explicit_size_big_and_small_test.v
Normal file
83
vlib/v/tests/enum_explicit_size_big_and_small_test.v
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
enum SmallEnum as i8 {
|
||||||
|
a = -1
|
||||||
|
b = -4
|
||||||
|
c
|
||||||
|
d = 5
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BigEnum as u64 {
|
||||||
|
a = 0xABCD_EF09_1234_5678
|
||||||
|
b = 0xFFFF_FFFF_FFFF_FFF0
|
||||||
|
c
|
||||||
|
d = 5
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BigIEnum as i64 {
|
||||||
|
a = -999_999_999_999
|
||||||
|
b = -900_000_000_000
|
||||||
|
c
|
||||||
|
d = 900_000_000_000
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_small_enum() {
|
||||||
|
dump(sizeof(SmallEnum))
|
||||||
|
$if tinyc {
|
||||||
|
// TODO: TCC currently ignores `__attribute__((packed))` for enums, and uses an int instead, even with -fshort-enums :-|
|
||||||
|
assert sizeof(SmallEnum) == 4
|
||||||
|
} $else {
|
||||||
|
assert sizeof(SmallEnum) == 1
|
||||||
|
}
|
||||||
|
dump(i8(SmallEnum.a))
|
||||||
|
dump(i8(SmallEnum.b))
|
||||||
|
dump(i8(SmallEnum.c))
|
||||||
|
dump(i8(SmallEnum.d))
|
||||||
|
dump(i8(SmallEnum.e))
|
||||||
|
assert i8(SmallEnum.a) == -1
|
||||||
|
assert i8(SmallEnum.b) == -4
|
||||||
|
assert i8(SmallEnum.c) == -3
|
||||||
|
assert i8(SmallEnum.d) == 5
|
||||||
|
assert i8(SmallEnum.e) == 6
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_big_enum() {
|
||||||
|
assert sizeof(BigIEnum) == 8
|
||||||
|
assert BigEnum.a.str() == 'a'
|
||||||
|
assert BigEnum.b.str() == 'b'
|
||||||
|
assert BigEnum.c.str() == 'c'
|
||||||
|
assert BigEnum.d.str() == 'd'
|
||||||
|
assert BigEnum.e.str() == 'e'
|
||||||
|
dump(u64(BigEnum.a))
|
||||||
|
dump(u64(BigEnum.b))
|
||||||
|
dump(u64(BigEnum.c))
|
||||||
|
dump(u64(BigEnum.d))
|
||||||
|
dump(u64(BigEnum.e))
|
||||||
|
assert sizeof(BigEnum) == 8
|
||||||
|
assert u64(BigEnum.a) == 0xABCD_EF09_1234_5678
|
||||||
|
assert u64(BigEnum.b) == 0xFFFF_FFFF_FFFF_FFF0
|
||||||
|
assert u64(BigEnum.c) == 0xFFFF_FFFF_FFFF_FFF1
|
||||||
|
assert u64(BigEnum.d) == 5
|
||||||
|
assert u64(BigEnum.e) == 6
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_big_ienum() {
|
||||||
|
assert sizeof(BigIEnum) == 8
|
||||||
|
assert BigIEnum.a.str() == 'a'
|
||||||
|
assert BigIEnum.b.str() == 'b'
|
||||||
|
assert BigIEnum.c.str() == 'c'
|
||||||
|
assert BigIEnum.d.str() == 'd'
|
||||||
|
assert BigIEnum.e.str() == 'e'
|
||||||
|
dump(i64(BigIEnum.a))
|
||||||
|
dump(i64(BigIEnum.b))
|
||||||
|
dump(i64(BigIEnum.c))
|
||||||
|
dump(i64(BigIEnum.d))
|
||||||
|
dump(i64(BigIEnum.e))
|
||||||
|
assert sizeof(BigIEnum) == 8
|
||||||
|
assert i64(BigIEnum.a) == -999999999999
|
||||||
|
assert i64(BigIEnum.b) == -900000000000
|
||||||
|
assert i64(BigIEnum.c) == -899999999999
|
||||||
|
assert i64(BigIEnum.d) == 900000000000
|
||||||
|
assert i64(BigIEnum.e) == 900000000001
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user