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
|
||||
|
||||
```v
|
||||
enum Color {
|
||||
enum Color as u8 {
|
||||
red
|
||||
green
|
||||
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.
|
||||
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
|
||||
|
||||
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 (
|
||||
echo In some cases, compile errors happen because of the MSVC compiler version
|
||||
cl.exe
|
||||
|
@ -118,7 +118,7 @@ pub enum ImageType {
|
||||
_num
|
||||
}
|
||||
|
||||
pub enum CubeFace {
|
||||
pub enum CubeFace as u32 {
|
||||
pos_x
|
||||
neg_x
|
||||
pos_y
|
||||
|
@ -1164,6 +1164,7 @@ pub:
|
||||
comments []Comment // comments before the first EnumField
|
||||
fields []EnumField // all the enum fields
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
*/
|
||||
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 {
|
||||
if !c.pref.experimental && util.contains_capital(field.name) {
|
||||
// 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',
|
||||
field.pos)
|
||||
}
|
||||
for j in 0 .. i {
|
||||
if field.name == node.fields[j].name {
|
||||
c.error('field name `$field.name` duplicate', field.pos)
|
||||
}
|
||||
if _ := seen_enum_field_names[field.name] {
|
||||
c.error('duplicate enum field name `$field.name`', field.pos)
|
||||
}
|
||||
seen_enum_field_names[field.name] = i
|
||||
if field.has_expr {
|
||||
match mut field.expr {
|
||||
ast.IntegerLiteral {
|
||||
val := field.expr.val.i64()
|
||||
if val < checker.int_min || val > checker.int_max {
|
||||
c.error('enum value `$val` overflows int', field.expr.pos)
|
||||
} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
|
||||
&& i64(val) in seen {
|
||||
c.error('enum value `$val` already exists', field.expr.pos)
|
||||
mut overflows := false
|
||||
mut uval := u64(0)
|
||||
mut ival := i64(0)
|
||||
if signed {
|
||||
val := field.expr.val.i64()
|
||||
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 {
|
||||
// Handle `enum Foo { x = 1 + 2 }`
|
||||
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 {
|
||||
if mut field.expr is ast.Ident {
|
||||
if field.expr.language == .c {
|
||||
@ -1449,21 +1530,38 @@ pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) {
|
||||
if pos.pos == 0 {
|
||||
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 {
|
||||
if seen.len > 0 {
|
||||
last := seen[seen.len - 1]
|
||||
if last == checker.int_max {
|
||||
c.error('enum value overflows', field.pos)
|
||||
} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed
|
||||
&& last + 1 in seen {
|
||||
c.error('enum value `${last + 1}` already exists', field.pos)
|
||||
if signed {
|
||||
if iseen.len > 0 {
|
||||
ilast := iseen[iseen.len - 1]
|
||||
if ilast == enum_imax {
|
||||
c.error('enum value overflows type `$senum_type`, which has a maximum value of $enum_imax',
|
||||
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 {
|
||||
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 mut node.expr is ast.IntegerLiteral {
|
||||
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] {
|
||||
mut in_range := false
|
||||
if enum_decl.is_flag {
|
||||
// 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
|
||||
in_range = node_val >= 0 && node_val <= max_val
|
||||
max_val := (u64(1) << enum_decl.fields.len) - 1
|
||||
in_range = node_val >= 0 && u64(node_val) <= max_val
|
||||
} else {
|
||||
mut enum_val := 0
|
||||
mut enum_val := i64(0)
|
||||
|
||||
for enum_field in enum_decl.fields {
|
||||
// check if the field of the enum value is an integer literal
|
||||
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 {
|
||||
in_range = true
|
||||
|
@ -1,5 +1,5 @@
|
||||
vlib/v/checker/tests/enum_err.vv:4:13: error: default value for enum has to be an integer
|
||||
2 |
|
||||
vlib/v/checker/tests/enum_err.vv:4:13: error: the default value for an enum has to be an integer
|
||||
2 |
|
||||
3 | enum Color {
|
||||
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
|
||||
4 | blue
|
||||
5 | green
|
||||
|
@ -1,6 +1,13 @@
|
||||
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
|
||||
3 | green = 2147483647
|
||||
4 | blue
|
||||
| ~~~~
|
||||
5 | }
|
||||
5 | }
|
||||
|
@ -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 {
|
||||
2 | red
|
||||
3 | green = 2147483648
|
||||
| ~~~~~~~~~~
|
||||
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
|
||||
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 {
|
||||
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 {
|
||||
f.writeln('enum $name {}\n')
|
||||
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('__', '.'))
|
||||
g.auto_str_funcs.writeln('\tstring ret = _SLIT("$clean_name{");')
|
||||
g.auto_str_funcs.writeln('\tint first = 1;')
|
||||
g.auto_str_funcs.writeln('\tu64 zit = (u64)it;')
|
||||
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('\treturn ret;')
|
||||
|
@ -3677,6 +3677,33 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
|
||||
fn (mut g Gen) enum_decl(node ast.EnumDecl) {
|
||||
enum_name := util.no_dots(node.name)
|
||||
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 {')
|
||||
mut cur_enum_expr := ''
|
||||
mut cur_enum_offset := 0
|
||||
@ -3690,8 +3717,9 @@ fn (mut g Gen) enum_decl(node ast.EnumDecl) {
|
||||
cur_enum_offset = 0
|
||||
} else if is_flag {
|
||||
g.enum_typedefs.write_string(' = ')
|
||||
cur_enum_expr = '1 << $i'
|
||||
g.enum_typedefs.write_string((1 << i).str())
|
||||
cur_enum_expr = 'u64(1) << $i'
|
||||
g.enum_typedefs.write_string((u64(1) << i).str())
|
||||
g.enum_typedefs.write_string('U')
|
||||
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')
|
||||
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) {
|
||||
|
@ -31,7 +31,7 @@ fn match_test() {
|
||||
assert false
|
||||
}
|
||||
}
|
||||
// println(color)
|
||||
// println(color)
|
||||
assert num == 3
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ fn (_ Bar) baz() {}
|
||||
|
||||
fn test_enum_variant_and_method_name_clash() {
|
||||
x := Bar.baz
|
||||
// println(x)
|
||||
// println(x)
|
||||
x.baz()
|
||||
}
|
||||
|
||||
@ -73,4 +73,5 @@ fn main() {
|
||||
match_test()
|
||||
test_enum_variant_and_method_name_clash()
|
||||
value_test()
|
||||
println('OK')
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
OK
|
@ -3665,8 +3665,14 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
||||
return ast.EnumDecl{}
|
||||
}
|
||||
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)
|
||||
enum_decl_comments := p.eat_comments()
|
||||
senum_type := p.table.get_type_name(enum_type)
|
||||
mut vals := []string{}
|
||||
// mut default_exprs := []ast.Expr{}
|
||||
mut fields := []ast.EnumField{}
|
||||
@ -3698,8 +3704,8 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
|
||||
is_flag := p.attrs.contains('flag')
|
||||
is_multi_allowed := p.attrs.contains('_allow_multiple_values')
|
||||
if is_flag {
|
||||
if fields.len > 32 {
|
||||
p.error('when an enum is used as bit field, it must have a max of 32 fields')
|
||||
if fields.len > 64 {
|
||||
p.error('when an enum is used as bit field, it must have a max of 64 fields')
|
||||
return ast.EnumDecl{}
|
||||
}
|
||||
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' }
|
||||
p.codegen('
|
||||
//
|
||||
[inline] $pubfn ( e &$enum_name) is_empty() bool { return int(*e) == 0 }
|
||||
[inline] $pubfn ( e &$enum_name) has(flag $enum_name) bool { return (int(*e) & (int(flag))) != 0 }
|
||||
[inline] $pubfn ( e &$enum_name) all(flag $enum_name) bool { return (int(*e) & (int(flag))) == int(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) clear(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}(int(*e) ^ (int(flag))) } }
|
||||
[inline] $pubfn ( e &$enum_name) is_empty() bool { return ${senum_type}(*e) == 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 (${senum_type}(*e) & (${senum_type}(flag))) == ${senum_type}(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}(${senum_type}(*e) & ~(${senum_type}(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{
|
||||
name: name
|
||||
typ: enum_type
|
||||
is_pub: is_pub
|
||||
is_flag: is_flag
|
||||
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…
Reference in New Issue
Block a user