From 9ef51ee714b185f2c0bc0ff057ace918f7d64f63 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 19 Jul 2025 18:47:44 +0300 Subject: [PATCH] v: virtual C consts with custom types (const C.MY_CONST u8) --- vlib/v/ast/ast.v | 13 ++++++------ vlib/v/checker/checker.v | 4 ++++ vlib/v/fmt/fmt.v | 12 ++++++++++-- vlib/v/parser/parser.v | 36 ++++++++++++++++++++++++++-------- vlib/v/tests/c_const_u8_test.v | 9 +++++++++ 5 files changed, 58 insertions(+), 16 deletions(-) create mode 100644 vlib/v/tests/c_const_u8_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index ae01e099a4..eb1e7989a8 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -398,12 +398,13 @@ pub fn (f &StructField) equals(o &StructField) bool { // const field in const declaration group pub struct ConstField { pub: - mod string - name string - is_pub bool - is_markused bool // an explicit `@[markused]` tag; the const will NOT be removed by `-skip-unused`, no matter what - pos token.Pos - attrs []Attr // same value as `attrs` of the ConstDecl to which it belongs + mod string + name string + is_pub bool + is_markused bool // an explicit `@[markused]` tag; the const will NOT be removed by `-skip-unused`, no matter what + pos token.Pos + attrs []Attr // same value as `attrs` of the ConstDecl to which it belongs + is_virtual_c bool // `const C.MY_CONST u8` pub mut: expr Expr // the value expr of field; everything after `=` typ Type // the type of the const field, it can be any type in V diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index df1e98c28b..f4dd3adac6 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4297,6 +4297,10 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { if node.name == 'C.NULL' { return ast.voidptr_type } + // TODO remove main. to avoid extra concats + if x := c.table.global_scope.find_const('main.' + node.name) { + return x.typ + } return ast.int_type } if c.inside_sql { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 7029ee7c98..b6811980f2 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -968,9 +968,17 @@ pub fn (mut f Fmt) const_decl(node ast.ConstDecl) { } f.write('const ') } + if field.is_virtual_c { + f.write('C.') + } f.write('${name} ') - f.write('= ') - f.expr(field.expr) + if field.is_virtual_c { + // f.typ(field.typ) + f.write(f.table.type_to_str(field.typ)) + } else { + f.write('= ') + f.expr(field.expr) + } f.comments(field.end_comments, same_line: true) if node.is_block && fidx < node.fields.len - 1 && node.fields.len > 1 { // old style grouped consts, converted to the new style ungrouped const diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 573024c7c8..9311f3595a 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2368,10 +2368,20 @@ fn (mut p Parser) const_decl() ast.ConstDecl { break } pos := p.tok.pos() - name := p.check_name() + mut name := p.check_name() end_comments << p.eat_comments() - if !p.pref.translated && !p.is_translated && util.contains_capital(name) { - p.error_with_pos('const names cannot contain uppercase letters, use snake_case instead', + // Handle `const C.MY_CONST u16` + mut is_virtual_c_const := false + mut typ := ast.void_type + if name == 'C' && p.tok.kind == .dot { + p.next() + name += '.' + p.check_name() + typ = p.parse_type() + is_virtual_c_const = true + } + if !p.pref.translated && !p.is_translated && util.contains_capital(name) + && !is_virtual_c_const { + p.error_with_pos('${name} const names cannot contain uppercase letters, use snake_case instead', pos) } full_name := p.prepend_mod(name) @@ -2379,14 +2389,17 @@ fn (mut p Parser) const_decl() ast.ConstDecl { p.error_with_pos('const declaration do not support multiple assign yet', p.tok.pos()) } // Allow for `const x := 123`, and for `const x = 123` too. - // Supporting `const x := 123` in addition to `const x = 123`, makes extracting local variables to constants much less annoying, while prototyping: + // Supporting `const x := 123` in addition to `const x = 123`, makes extracting local variables to constants + // much less annoying, while prototyping: if p.tok.kind == .decl_assign { p.check(.decl_assign) } else { - p.check(.assign) + if !is_virtual_c_const { + p.check(.assign) + } } end_comments << p.eat_comments() - if p.tok.kind == .key_fn { + if p.tok.kind == .key_fn && !is_virtual_c_const { p.error('const initializer fn literal is not a constant') return ast.ConstDecl{} } @@ -2394,11 +2407,14 @@ fn (mut p Parser) const_decl() ast.ConstDecl { p.unexpected(got: 'eof', expecting: 'an expression') return ast.ConstDecl{} } - expr := p.expr(0) + mut expr := ast.Expr{} + if !is_virtual_c_const { + expr = p.expr(0) + } if is_block { end_comments << p.eat_comments(same_line: true) } - field := ast.ConstField{ + mut field := ast.ConstField{ name: full_name mod: p.mod is_pub: is_pub @@ -2408,6 +2424,10 @@ fn (mut p Parser) const_decl() ast.ConstDecl { comments: comments end_comments: end_comments is_markused: is_markused + is_virtual_c: is_virtual_c_const + } + if is_virtual_c_const { + field.typ = typ } fields << field p.table.global_scope.register(field) diff --git a/vlib/v/tests/c_const_u8_test.v b/vlib/v/tests/c_const_u8_test.v new file mode 100644 index 0000000000..7e9f9ce4d1 --- /dev/null +++ b/vlib/v/tests/c_const_u8_test.v @@ -0,0 +1,9 @@ +const C.AF_INET u16 + +fn x(n u16) bool { + return true +} + +fn test_const() { + assert x(C.AF_INET) == true +}