From 24af002249a170fda0429fdc1481860866345b6c Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sun, 31 Mar 2024 03:14:33 -0300 Subject: [PATCH] v,breaking: add ability to read enum, fn, interface and sumtype attributes in compile-time, change builtin `StructAttribute` to `VAttribute` (#21149) --- vlib/builtin/builtin.v | 2 +- vlib/db/mysql/mysql_orm_test.v | 4 +- vlib/db/pg/pg_orm_test.v | 4 +- vlib/db/sqlite/sqlite_orm_test.v | 4 +- vlib/orm/orm.v | 2 +- vlib/orm/orm_fn_test.v | 22 ++++----- vlib/v/ast/table.v | 29 ++++++++++++ vlib/v/gen/c/comptime.v | 10 ++-- vlib/v/gen/c/orm.v | 6 +-- vlib/v/parser/comptime.v | 2 +- vlib/v/parser/parser.v | 4 +- vlib/v/tests/comptime_attr_test.v | 78 +++++++++++++++++++++++++++++++ vlib/v/tests/comptime_dump_test.v | 2 +- 13 files changed, 139 insertions(+), 30 deletions(-) create mode 100644 vlib/v/tests/comptime_attr_test.v diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index a88144167f2141..651b7167059e3c 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -151,7 +151,7 @@ pub enum AttributeKind { comptime_define // [if name] } -pub struct StructAttribute { +pub struct VAttribute { pub: name string has_arg bool diff --git a/vlib/db/mysql/mysql_orm_test.v b/vlib/db/mysql/mysql_orm_test.v index f9cc0e289f3b1f..fabde536509d0a 100644 --- a/vlib/db/mysql/mysql_orm_test.v +++ b/vlib/db/mysql/mysql_orm_test.v @@ -51,10 +51,10 @@ fn test_mysql_orm() { name: 'id' typ: typeof[int]().idx attrs: [ - StructAttribute{ + VAttribute{ name: 'primary' }, - StructAttribute{ + VAttribute{ name: 'sql' has_arg: true kind: .plain diff --git a/vlib/db/pg/pg_orm_test.v b/vlib/db/pg/pg_orm_test.v index dfc285667400ee..fd983ee0694246 100644 --- a/vlib/db/pg/pg_orm_test.v +++ b/vlib/db/pg/pg_orm_test.v @@ -55,13 +55,13 @@ fn test_pg_orm() { default_val: '' is_arr: false attrs: [ - StructAttribute{ + VAttribute{ name: 'primary' has_arg: false arg: '' kind: .plain }, - StructAttribute{ + VAttribute{ name: 'sql' has_arg: true arg: 'serial' diff --git a/vlib/db/sqlite/sqlite_orm_test.v b/vlib/db/sqlite/sqlite_orm_test.v index e832bfb32ae22d..081104593328c3 100644 --- a/vlib/db/sqlite/sqlite_orm_test.v +++ b/vlib/db/sqlite/sqlite_orm_test.v @@ -36,10 +36,10 @@ fn test_sqlite_orm() { name: 'id' typ: typeof[int]().idx attrs: [ - StructAttribute{ + VAttribute{ name: 'primary' }, - StructAttribute{ + VAttribute{ name: 'sql' has_arg: true kind: .plain diff --git a/vlib/orm/orm.v b/vlib/orm/orm.v index 97c43153fd4126..6f11f42349a90f 100644 --- a/vlib/orm/orm.v +++ b/vlib/orm/orm.v @@ -152,7 +152,7 @@ pub: typ int nullable bool default_val string - attrs []StructAttribute + attrs []VAttribute is_arr bool } diff --git a/vlib/orm/orm_fn_test.v b/vlib/orm/orm_fn_test.v index 00cfc8b054189c..dd3b11379c587d 100644 --- a/vlib/orm/orm_fn_test.v +++ b/vlib/orm/orm_fn_test.v @@ -156,10 +156,10 @@ fn test_orm_table_gen() { default_val: '10' nullable: true attrs: [ - StructAttribute{ + VAttribute{ name: 'primary' }, - StructAttribute{ + VAttribute{ name: 'sql' has_arg: true arg: 'serial' @@ -188,10 +188,10 @@ fn test_orm_table_gen() { nullable: true default_val: '10' attrs: [ - StructAttribute{ + VAttribute{ name: 'primary' }, - StructAttribute{ + VAttribute{ name: 'sql' has_arg: true arg: 'serial' @@ -220,10 +220,10 @@ fn test_orm_table_gen() { nullable: true default_val: '10' attrs: [ - StructAttribute{ + VAttribute{ name: 'primary' }, - StructAttribute{ + VAttribute{ name: 'sql' has_arg: true arg: 'serial' @@ -235,7 +235,7 @@ fn test_orm_table_gen() { name: 'test' typ: typeof[string]().idx attrs: [ - StructAttribute{ + VAttribute{ name: 'unique' }, ] @@ -255,10 +255,10 @@ fn test_orm_table_gen() { nullable: true default_val: '10' attrs: [ - StructAttribute{ + VAttribute{ name: 'primary' }, - StructAttribute{ + VAttribute{ name: 'sql' has_arg: true arg: 'serial' @@ -271,7 +271,7 @@ fn test_orm_table_gen() { typ: typeof[string]().idx nullable: true attrs: [ - StructAttribute{ + VAttribute{ name: 'unique' has_arg: true arg: 'test' @@ -285,7 +285,7 @@ fn test_orm_table_gen() { nullable: true default_val: '6754' attrs: [ - StructAttribute{ + VAttribute{ name: 'unique' has_arg: true arg: 'test' diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 8d4065e0da94c8..59b613683e597e 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -24,6 +24,7 @@ pub mut: redefined_fns []string fn_generic_types map[string][][]Type // for generic functions interfaces map[int]InterfaceDecl + sumtypes map[int]SumTypeDecl cmod_prefix string // needed for ast.type_to_str(Type) while vfmt; contains `os.` is_fmt bool used_fns map[string]bool // filled in by the checker, when pref.skip_unused = true; @@ -222,6 +223,10 @@ pub fn (mut t Table) register_interface(idecl InterfaceDecl) { t.interfaces[idecl.typ] = idecl } +pub fn (mut t Table) register_sumtype(sumtyp SumTypeDecl) { + t.sumtypes[sumtyp.typ] = sumtyp +} + pub fn (mut t TypeSymbol) register_method(new_fn Fn) int { // returns a method index, stored in the ast.FnDecl // for faster lookup in the checker's fn_decl method @@ -2519,3 +2524,27 @@ pub fn (t &Table) get_trace_fn_name(cur_fn FnDecl, node CallExpr) (string, strin } return hash_fn, fn_name } + +// get_attrs retrieve the attribrutes from the type symbol +pub fn (t &Table) get_attrs(sym TypeSymbol) []Attr { + match sym.info { + Enum { + return t.enum_decls[sym.name].attrs + } + Struct { + return sym.info.attrs + } + FnType { + return sym.info.func.attrs + } + Interface { + return unsafe { t.interfaces[sym.idx].attrs } + } + SumType { + return unsafe { t.sumtypes[sym.idx].attrs } + } + else { + return [] + } + } +} diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index bf1260a899aa7a..f3256b104296c1 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -932,11 +932,11 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { } } } else if node.kind == .attributes { - if sym.info is ast.Struct { - if sym.info.attrs.len > 0 { - g.writeln('\tStructAttribute ${node.val_var} = {0};') - } - for attr in sym.info.attrs { + attrs := g.table.get_attrs(sym) + if attrs.len > 0 { + g.writeln('\tVAttribute ${node.val_var} = {0};') + + for attr in attrs { g.writeln('/* attribute ${i} */ {') g.writeln('\t${node.val_var}.name = _SLIT("${attr.name}");') g.writeln('\t${node.val_var}.has_arg = ${attr.has_arg};') diff --git a/vlib/v/gen/c/orm.v b/vlib/v/gen/c/orm.v index 25936c1a6f0e24..d769c917d02285 100644 --- a/vlib/v/gen/c/orm.v +++ b/vlib/v/gen/c/orm.v @@ -170,14 +170,14 @@ fn (mut g Gen) write_orm_create_table(node ast.SqlStmtLine, table_name string, c g.writeln('.is_arr = ${sym.kind == .array}, ') g.writeln('.nullable = ${field.typ.has_flag(.option)},') g.writeln('.default_val = (string){ .str = (byteptr) "${field.default_val}", .is_lit = 1 },') - g.writeln('.attrs = new_array_from_c_array(${field.attrs.len}, ${field.attrs.len}, sizeof(StructAttribute),') + g.writeln('.attrs = new_array_from_c_array(${field.attrs.len}, ${field.attrs.len}, sizeof(VAttribute),') g.indent++ if field.attrs.len > 0 { - g.write('_MOV((StructAttribute[${field.attrs.len}]){') + g.write('_MOV((VAttribute[${field.attrs.len}]){') g.indent++ for attr in field.attrs { - g.write('(StructAttribute){') + g.write('(VAttribute){') g.indent++ g.write(' .name = _SLIT("${attr.name}"),') g.write(' .has_arg = ${attr.has_arg},') diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 7f3079b6b9e76d..b2347633561353 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -360,7 +360,7 @@ fn (mut p Parser) comptime_for() ast.ComptimeFor { 'attributes' { p.scope.register(ast.Var{ name: val_var - typ: p.table.find_type_idx('StructAttribute') + typ: p.table.find_type_idx('VAttribute') pos: var_pos }) kind = .attributes diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 0af88768cdca32..67b2547fa8d7a0 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -4409,7 +4409,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { name_pos) return ast.SumTypeDecl{} } - return ast.SumTypeDecl{ + node := ast.SumTypeDecl{ name: name typ: typ is_pub: is_pub @@ -4419,6 +4419,8 @@ fn (mut p Parser) type_decl() ast.TypeDecl { pos: decl_pos name_pos: name_pos } + p.table.register_sumtype(node) + return node } // type MyType = int if generic_types.len > 0 { diff --git a/vlib/v/tests/comptime_attr_test.v b/vlib/v/tests/comptime_attr_test.v new file mode 100644 index 00000000000000..3c13a33aebfa60 --- /dev/null +++ b/vlib/v/tests/comptime_attr_test.v @@ -0,0 +1,78 @@ +@[bar; foo] +fn abc() { +} + +@[foo] +struct Foo { +} + +@[bar] +fn Foo.bar() { +} + +@[baz] +enum EnumFoo { + a + b +} + +@[iface] +interface IFoo { +} + +@[custom] +type CustomType = EnumFoo | Foo + +fn test_main() { + mut c := 0 + $for f in abc.attributes { + dump(f) + assert f.name in ['foo', 'bar'] + c++ + } + assert c == 2 + + $for f in Foo.attributes { + dump(f) + assert f.name == 'foo' + c++ + } + assert c == 3 + + a := Foo.bar + $for f in a.attributes { + dump(f) + assert f.name == 'bar' + c++ + } + assert c == 4 + + b := abc + $for f in b.attributes { + dump(f) + assert f.name in ['foo', 'bar'] + c++ + } + assert c == 6 + + $for f in EnumFoo.attributes { + dump(f) + assert f.name == 'baz' + c++ + } + assert c == 7 + + $for f in IFoo.attributes { + dump(f) + assert f.name == 'iface' + c++ + } + assert c == 8 + + $for f in CustomType.attributes { + dump(f) + assert f.name == 'custom' + c++ + } + assert c == 9 +} diff --git a/vlib/v/tests/comptime_dump_test.v b/vlib/v/tests/comptime_dump_test.v index 4fd64e64703469..94bcc8c22fbfcd 100644 --- a/vlib/v/tests/comptime_dump_test.v +++ b/vlib/v/tests/comptime_dump_test.v @@ -42,7 +42,7 @@ fn test_main() { dump(f) dump(f.name) c += 1 - assert typeof(f).name == 'StructAttribute' + assert typeof(f).name == 'VAttribute' } assert c == 8 }