mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
all: anonymous structs (part 2)
This commit is contained in:
parent
f4b39fbe4f
commit
426421bebb
10 changed files with 151 additions and 99 deletions
|
@ -1,9 +1,15 @@
|
||||||
## V 0.3.1
|
## V 0.3.1
|
||||||
*Not released yet*
|
*Not released yet*
|
||||||
|
- Anonymous structs.
|
||||||
- V can now find code in the `src/` directory. This allows making V repos much cleaner.
|
- V can now find code in the `src/` directory. This allows making V repos much cleaner.
|
||||||
- `os.mkdir()` now has an optional `mode` paramter.
|
- `os.mkdir()` now has an optional `mode` paramter.
|
||||||
- Full termux support via `$if termux {`.
|
- Full termux support via `$if termux {`.
|
||||||
- Go backend fixes.
|
- Go backend fixes.
|
||||||
|
- More type checks.
|
||||||
|
- New keyword/type: `nil`. Only to be used inside `unsafe`. Replaces `voidptr(0)`.
|
||||||
|
- DOOM is now translated/compiled and launched on CI servers. A screenshot of the running game
|
||||||
|
is made via `vgret` and is compared to the expected result.
|
||||||
|
- VLS performance improvements, especially on Windows.
|
||||||
|
|
||||||
## V 0.3
|
## V 0.3
|
||||||
*30 Jun 2022*
|
*30 Jun 2022*
|
||||||
|
|
|
@ -5,8 +5,12 @@
|
||||||
- [ ] Parallel checker
|
- [ ] Parallel checker
|
||||||
- [ ] Parallel C compilation
|
- [ ] Parallel C compilation
|
||||||
- [ ] `recover()` from panics
|
- [ ] `recover()` from panics
|
||||||
- [ ] vfmt: fix common errors automatically (make vars mutable and vice versa, add missing imports)
|
- [ ] vfmt: add missing imports (like goimports)
|
||||||
- [ ] merge v.c and v_win.c
|
- [ ] merge v.c and v_win.c
|
||||||
- [ ] Recursive structs via optionals: `struct Node { next ?Node }`
|
- [ ] Recursive structs via optionals: `struct Node { next ?Node }`
|
||||||
- [ ] Handle function pointers safely, remove `if function == 0 {`
|
- [ ] Handle function pointers safely, remove `if function == 0 {`
|
||||||
- [ ] Bundle OpenSSL like GC
|
- [ ] Bundle OpenSSL like GC
|
||||||
|
- [ ] Anonymous structs
|
||||||
|
- [ ] -usecache on by default
|
||||||
|
- [ ] -skip-unused on by default
|
||||||
|
|
||||||
|
|
|
@ -1020,6 +1020,7 @@ pub mut:
|
||||||
is_union bool
|
is_union bool
|
||||||
is_heap bool
|
is_heap bool
|
||||||
is_minify bool
|
is_minify bool
|
||||||
|
is_anon bool
|
||||||
is_generic bool
|
is_generic bool
|
||||||
generic_types []Type
|
generic_types []Type
|
||||||
concrete_types []Type
|
concrete_types []Type
|
||||||
|
|
|
@ -5,12 +5,12 @@ module checker
|
||||||
import v.ast
|
import v.ast
|
||||||
|
|
||||||
pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
pub fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
||||||
if node.language == .v && !c.is_builtin_mod {
|
|
||||||
c.check_valid_pascal_case(node.name, 'struct name', node.pos)
|
|
||||||
}
|
|
||||||
mut struct_sym, struct_typ_idx := c.table.find_sym_and_type_idx(node.name)
|
mut struct_sym, struct_typ_idx := c.table.find_sym_and_type_idx(node.name)
|
||||||
mut has_generic_types := false
|
mut has_generic_types := false
|
||||||
if mut struct_sym.info is ast.Struct {
|
if mut struct_sym.info is ast.Struct {
|
||||||
|
if node.language == .v && !c.is_builtin_mod && !struct_sym.info.is_anon {
|
||||||
|
c.check_valid_pascal_case(node.name, 'struct name', node.pos)
|
||||||
|
}
|
||||||
for embed in node.embeds {
|
for embed in node.embeds {
|
||||||
if embed.typ.has_flag(.generic) {
|
if embed.typ.has_flag(.generic) {
|
||||||
has_generic_types = true
|
has_generic_types = true
|
||||||
|
|
|
@ -4927,96 +4927,7 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
|
||||||
mut name := sym.cname
|
mut name := sym.cname
|
||||||
match sym.info {
|
match sym.info {
|
||||||
ast.Struct {
|
ast.Struct {
|
||||||
if sym.info.is_generic {
|
g.struct_decl(sym.info, name, false)
|
||||||
continue
|
|
||||||
}
|
|
||||||
if name.contains('_T_') {
|
|
||||||
g.typedefs.writeln('typedef struct $name $name;')
|
|
||||||
}
|
|
||||||
// TODO avoid buffer manip
|
|
||||||
start_pos := g.type_definitions.len
|
|
||||||
|
|
||||||
mut pre_pragma := ''
|
|
||||||
mut post_pragma := ''
|
|
||||||
|
|
||||||
for attr in sym.info.attrs {
|
|
||||||
match attr.name {
|
|
||||||
'_pack' {
|
|
||||||
pre_pragma += '#pragma pack(push, $attr.arg)\n'
|
|
||||||
post_pragma += '#pragma pack(pop)'
|
|
||||||
}
|
|
||||||
else {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is_minify := sym.info.is_minify
|
|
||||||
g.type_definitions.writeln(pre_pragma)
|
|
||||||
|
|
||||||
if sym.info.is_union {
|
|
||||||
g.type_definitions.writeln('union $name {')
|
|
||||||
} else {
|
|
||||||
g.type_definitions.writeln('struct $name {')
|
|
||||||
}
|
|
||||||
if sym.info.fields.len > 0 || sym.info.embeds.len > 0 {
|
|
||||||
for field in sym.info.fields {
|
|
||||||
// Some of these structs may want to contain
|
|
||||||
// optionals that may not be defined at this point
|
|
||||||
// if this is the case then we are going to
|
|
||||||
// buffer manip out in front of the struct
|
|
||||||
// write the optional in and then continue
|
|
||||||
// FIXME: for parallel cgen (two different files using the same optional in struct fields)
|
|
||||||
if field.typ.has_flag(.optional) {
|
|
||||||
// Dont use g.typ() here becuase it will register
|
|
||||||
// optional and we dont want that
|
|
||||||
styp, base := g.optional_type_name(field.typ)
|
|
||||||
lock g.done_optionals {
|
|
||||||
if base !in g.done_optionals {
|
|
||||||
g.done_optionals << base
|
|
||||||
last_text := g.type_definitions.after(start_pos).clone()
|
|
||||||
g.type_definitions.go_back_to(start_pos)
|
|
||||||
g.typedefs.writeln('typedef struct $styp $styp;')
|
|
||||||
g.type_definitions.writeln('${g.optional_type_text(styp,
|
|
||||||
base)};')
|
|
||||||
g.type_definitions.write_string(last_text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type_name := g.typ(field.typ)
|
|
||||||
field_name := c_name(field.name)
|
|
||||||
volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
|
|
||||||
mut size_suffix := ''
|
|
||||||
if is_minify && !g.is_cc_msvc {
|
|
||||||
if field.typ == ast.bool_type_idx {
|
|
||||||
size_suffix = ' : 1'
|
|
||||||
} else {
|
|
||||||
field_sym := g.table.sym(field.typ)
|
|
||||||
if field_sym.info is ast.Enum {
|
|
||||||
if !field_sym.info.is_flag && !field_sym.info.uses_exprs {
|
|
||||||
mut bits_needed := 0
|
|
||||||
mut l := field_sym.info.vals.len
|
|
||||||
for l > 0 {
|
|
||||||
bits_needed++
|
|
||||||
l >>= 1
|
|
||||||
}
|
|
||||||
size_suffix = ' : $bits_needed'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.type_definitions.writeln('\t$volatile_prefix$type_name $field_name$size_suffix;')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g.type_definitions.writeln('\tEMPTY_STRUCT_DECLARATION;')
|
|
||||||
}
|
|
||||||
// g.type_definitions.writeln('} $name;\n')
|
|
||||||
//
|
|
||||||
ti_attrs := if sym.info.attrs.contains('packed') {
|
|
||||||
'__attribute__((__packed__))'
|
|
||||||
} else {
|
|
||||||
''
|
|
||||||
}
|
|
||||||
g.type_definitions.writeln('}$ti_attrs;\n')
|
|
||||||
g.type_definitions.writeln(post_pragma)
|
|
||||||
}
|
}
|
||||||
ast.Alias {
|
ast.Alias {
|
||||||
// ast.Alias { TODO
|
// ast.Alias { TODO
|
||||||
|
|
|
@ -311,3 +311,114 @@ fn (mut g Gen) is_empty_struct(t Type) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
|
||||||
|
if s.is_generic {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if name.contains('_T_') {
|
||||||
|
g.typedefs.writeln('typedef struct $name $name;')
|
||||||
|
}
|
||||||
|
// TODO avoid buffer manip
|
||||||
|
start_pos := g.type_definitions.len
|
||||||
|
|
||||||
|
mut pre_pragma := ''
|
||||||
|
mut post_pragma := ''
|
||||||
|
|
||||||
|
for attr in s.attrs {
|
||||||
|
match attr.name {
|
||||||
|
'_pack' {
|
||||||
|
pre_pragma += '#pragma pack(push, $attr.arg)\n'
|
||||||
|
post_pragma += '#pragma pack(pop)'
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_minify := s.is_minify
|
||||||
|
g.type_definitions.writeln(pre_pragma)
|
||||||
|
|
||||||
|
if is_anon {
|
||||||
|
g.type_definitions.writeln('struct {')
|
||||||
|
} else if s.is_union {
|
||||||
|
g.type_definitions.writeln('union $name {')
|
||||||
|
} else {
|
||||||
|
g.type_definitions.writeln('struct $name {')
|
||||||
|
}
|
||||||
|
if s.fields.len > 0 || s.embeds.len > 0 {
|
||||||
|
for field in s.fields {
|
||||||
|
// Some of these structs may want to contain
|
||||||
|
// optionals that may not be defined at this point
|
||||||
|
// if this is the case then we are going to
|
||||||
|
// buffer manip out in front of the struct
|
||||||
|
// write the optional in and then continue
|
||||||
|
// FIXME: for parallel cgen (two different files using the same optional in struct fields)
|
||||||
|
if field.typ.has_flag(.optional) {
|
||||||
|
// Dont use g.typ() here becuase it will register
|
||||||
|
// optional and we dont want that
|
||||||
|
styp, base := g.optional_type_name(field.typ)
|
||||||
|
lock g.done_optionals {
|
||||||
|
if base !in g.done_optionals {
|
||||||
|
g.done_optionals << base
|
||||||
|
last_text := g.type_definitions.after(start_pos).clone()
|
||||||
|
g.type_definitions.go_back_to(start_pos)
|
||||||
|
g.typedefs.writeln('typedef struct $styp $styp;')
|
||||||
|
g.type_definitions.writeln('${g.optional_type_text(styp, base)};')
|
||||||
|
g.type_definitions.write_string(last_text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type_name := g.typ(field.typ)
|
||||||
|
field_name := c_name(field.name)
|
||||||
|
volatile_prefix := if field.is_volatile { 'volatile ' } else { '' }
|
||||||
|
mut size_suffix := ''
|
||||||
|
if is_minify && !g.is_cc_msvc {
|
||||||
|
if field.typ == ast.bool_type_idx {
|
||||||
|
size_suffix = ' : 1'
|
||||||
|
} else {
|
||||||
|
field_sym := g.table.sym(field.typ)
|
||||||
|
if field_sym.info is ast.Enum {
|
||||||
|
if !field_sym.info.is_flag && !field_sym.info.uses_exprs {
|
||||||
|
mut bits_needed := 0
|
||||||
|
mut l := field_sym.info.vals.len
|
||||||
|
for l > 0 {
|
||||||
|
bits_needed++
|
||||||
|
l >>= 1
|
||||||
|
}
|
||||||
|
size_suffix = ' : $bits_needed'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field_sym := g.table.sym(field.typ)
|
||||||
|
mut field_is_anon := false
|
||||||
|
if field_sym.info is ast.Struct {
|
||||||
|
if field_sym.info.is_anon {
|
||||||
|
field_is_anon = true
|
||||||
|
// Recursively generate code for this anon struct (this is the field's type)
|
||||||
|
g.struct_decl(field_sym.info, field_sym.cname, true)
|
||||||
|
// Now the field's name
|
||||||
|
g.type_definitions.writeln(' $field_name$size_suffix;')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !field_is_anon {
|
||||||
|
g.type_definitions.writeln('\t$volatile_prefix$type_name $field_name$size_suffix;')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.type_definitions.writeln('\tEMPTY_STRUCT_DECLARATION;')
|
||||||
|
}
|
||||||
|
// g.type_definitions.writeln('} $name;\n')
|
||||||
|
//
|
||||||
|
ti_attrs := if s.attrs.contains('packed') {
|
||||||
|
'__attribute__((__packed__))'
|
||||||
|
} else {
|
||||||
|
''
|
||||||
|
}
|
||||||
|
g.type_definitions.write_string('}$ti_attrs')
|
||||||
|
if !is_anon {
|
||||||
|
g.type_definitions.write_string(';')
|
||||||
|
}
|
||||||
|
g.type_definitions.writeln('\n')
|
||||||
|
g.type_definitions.writeln(post_pragma)
|
||||||
|
}
|
||||||
|
|
|
@ -425,9 +425,9 @@ pub fn (mut p Parser) parse_type() ast.Type {
|
||||||
}
|
}
|
||||||
// Anon structs
|
// Anon structs
|
||||||
if p.tok.kind == .key_struct {
|
if p.tok.kind == .key_struct {
|
||||||
p.struct_decl(true)
|
struct_decl := p.struct_decl(true)
|
||||||
p.next()
|
// Find the registered anon struct type, it was registered above in `p.struct_decl()`
|
||||||
return p.table.find_type_idx('anon_struct') // TODO
|
return p.table.find_type_idx(struct_decl.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
language := p.parse_language()
|
language := p.parse_language()
|
||||||
|
|
|
@ -91,6 +91,7 @@ mut:
|
||||||
if_cond_comments []ast.Comment
|
if_cond_comments []ast.Comment
|
||||||
script_mode bool
|
script_mode bool
|
||||||
script_mode_start_token token.Token
|
script_mode_start_token token.Token
|
||||||
|
anon_struct_counter int
|
||||||
pub mut:
|
pub mut:
|
||||||
scanner &scanner.Scanner
|
scanner &scanner.Scanner
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
|
|
|
@ -39,7 +39,12 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
||||||
if p.disallow_declarations_in_script_mode() {
|
if p.disallow_declarations_in_script_mode() {
|
||||||
return ast.StructDecl{}
|
return ast.StructDecl{}
|
||||||
}
|
}
|
||||||
mut name := if is_anon { '' } else { p.check_name() }
|
mut name := if is_anon {
|
||||||
|
p.anon_struct_counter++
|
||||||
|
'_VAnonStruct$p.anon_struct_counter'
|
||||||
|
} else {
|
||||||
|
p.check_name()
|
||||||
|
}
|
||||||
if name.len == 1 && name[0].is_capital() {
|
if name.len == 1 && name[0].is_capital() {
|
||||||
p.error_with_pos('single letter capital names are reserved for generic template types.',
|
p.error_with_pos('single letter capital names are reserved for generic template types.',
|
||||||
name_pos)
|
name_pos)
|
||||||
|
@ -61,7 +66,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
||||||
return ast.StructDecl{}
|
return ast.StructDecl{}
|
||||||
}
|
}
|
||||||
if language == .v && !p.builtin_mod && !p.is_translated && name.len > 0 && !name[0].is_capital()
|
if language == .v && !p.builtin_mod && !p.is_translated && name.len > 0 && !name[0].is_capital()
|
||||||
&& !p.pref.translated && !p.is_translated {
|
&& !p.pref.translated && !p.is_translated && !is_anon {
|
||||||
p.error_with_pos('struct name `$name` must begin with capital letter', name_pos)
|
p.error_with_pos('struct name `$name` must begin with capital letter', name_pos)
|
||||||
return ast.StructDecl{}
|
return ast.StructDecl{}
|
||||||
}
|
}
|
||||||
|
@ -338,6 +343,7 @@ fn (mut p Parser) struct_decl(is_anon bool) ast.StructDecl {
|
||||||
is_generic: generic_types.len > 0
|
is_generic: generic_types.len > 0
|
||||||
generic_types: generic_types
|
generic_types: generic_types
|
||||||
attrs: attrs
|
attrs: attrs
|
||||||
|
is_anon: is_anon
|
||||||
}
|
}
|
||||||
is_pub: is_pub
|
is_pub: is_pub
|
||||||
}
|
}
|
||||||
|
|
|
@ -412,3 +412,15 @@ fn test_struct_update() {
|
||||||
assert c2.capital.name == 'city'
|
assert c2.capital.name == 'city'
|
||||||
assert c2.name == 'test'
|
assert c2.name == 'test'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test anon structs
|
||||||
|
struct Book {
|
||||||
|
x Foo
|
||||||
|
title string
|
||||||
|
author struct {
|
||||||
|
name string
|
||||||
|
age int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_anon() {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue