1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

ast, cgen: fix wrong name and cname, when array and fixed array elements have optional (fix #16099) (#16122)

This commit is contained in:
shove 2022-10-20 19:17:57 +08:00 committed by GitHub
parent 2083e6b04c
commit 07310d850d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 133 additions and 31 deletions

View File

@ -884,21 +884,22 @@ pub fn (t &Table) known_type_idx(typ Type) bool {
pub fn (t &Table) array_name(elem_type Type) string {
elem_type_sym := t.sym(elem_type)
ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' }
return '[]$ptr$elem_type_sym.name'
opt := if elem_type.has_flag(.optional) { '?' } else { '' }
res := if elem_type.has_flag(.result) { '!' } else { '' }
return '[]$opt$res$ptr$elem_type_sym.name'
}
[inline]
pub fn (t &Table) array_cname(elem_type Type) string {
elem_type_sym := t.sym(elem_type)
mut res := ''
if elem_type.is_ptr() {
res = '_ptr'.repeat(elem_type.nr_muls())
}
suffix := if elem_type.is_ptr() { '_ptr'.repeat(elem_type.nr_muls()) } else { '' }
opt := if elem_type.has_flag(.optional) { '_option_' } else { '' }
res := if elem_type.has_flag(.result) { '_result_' } else { '' }
if elem_type_sym.cname.contains('<') {
type_name := elem_type_sym.cname.replace_each(['<', '_T_', ', ', '_', '>', ''])
return 'Array_$type_name' + res
return 'Array_$opt$res$type_name$suffix'
} else {
return 'Array_$elem_type_sym.cname' + res
return 'Array_$opt$res$elem_type_sym.cname$suffix'
}
}
@ -908,22 +909,28 @@ pub fn (t &Table) array_cname(elem_type Type) string {
pub fn (t &Table) array_fixed_name(elem_type Type, size int, size_expr Expr) string {
elem_type_sym := t.sym(elem_type)
ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' }
opt := if elem_type.has_flag(.optional) { '?' } else { '' }
res := if elem_type.has_flag(.result) { '!' } else { '' }
size_str := if size_expr is EmptyExpr || size != 987654321 {
size.str()
} else {
size_expr.str()
}
return '[$size_str]$ptr$elem_type_sym.name'
return '[$size_str]$opt$res$ptr$elem_type_sym.name'
}
[inline]
pub fn (t &Table) array_fixed_cname(elem_type Type, size int) string {
elem_type_sym := t.sym(elem_type)
mut res := ''
if elem_type.is_ptr() {
res = '_ptr$elem_type.nr_muls()'
suffix := if elem_type.is_ptr() { '_ptr$elem_type.nr_muls()' } else { '' }
opt := if elem_type.has_flag(.optional) { '_option_' } else { '' }
res := if elem_type.has_flag(.result) { '_result_' } else { '' }
if elem_type_sym.cname.contains('<') {
type_name := elem_type_sym.cname.replace_each(['<', '_T_', ', ', '_', '>', ''])
return 'Array_fixed_$opt$res$type_name${suffix}_$size'
} else {
return 'Array_fixed_$opt$res$elem_type_sym.cname${suffix}_$size'
}
return 'Array_fixed_$elem_type_sym.cname${res}_$size'
}
[inline]
@ -1012,7 +1019,7 @@ pub fn (t &Table) thread_cname(return_type Type) string {
pub fn (t &Table) map_name(key_type Type, value_type Type) string {
key_type_sym := t.sym(key_type)
value_type_sym := t.sym(value_type)
ptr := if value_type.is_ptr() { '&' } else { '' }
ptr := if value_type.is_ptr() { '&'.repeat(value_type.nr_muls()) } else { '' }
opt := if value_type.has_flag(.optional) { '?' } else { '' }
res := if value_type.has_flag(.result) { '!' } else { '' }
return 'map[$key_type_sym.name]$opt$res$ptr$value_type_sym.name'
@ -1022,11 +1029,16 @@ pub fn (t &Table) map_name(key_type Type, value_type Type) string {
pub fn (t &Table) map_cname(key_type Type, value_type Type) string {
key_type_sym := t.sym(key_type)
value_type_sym := t.sym(value_type)
suffix := if value_type.is_ptr() { '_ptr' } else { '' }
suffix := if value_type.is_ptr() { '_ptr'.repeat(value_type.nr_muls()) } else { '' }
opt := if value_type.has_flag(.optional) { '_option_' } else { '' }
res := if value_type.has_flag(.result) { '_result_' } else { '' }
if value_type_sym.cname.contains('<') {
type_name := value_type_sym.cname.replace_each(['<', '_T_', ', ', '_', '>', ''])
return 'Map_${key_type_sym.cname}_$opt$res$type_name$suffix'
} else {
return 'Map_${key_type_sym.cname}_$opt$res$value_type_sym.cname$suffix'
}
}
pub fn (mut t Table) find_or_register_chan(elem_type Type, is_mut bool) int {
name := t.chan_name(elem_type, is_mut)

View File

@ -72,7 +72,9 @@ mut:
embedded_data strings.Builder // data to embed in the executable/binary
shared_types strings.Builder // shared/lock types
shared_functions strings.Builder // shared constructors
options strings.Builder // `option_xxxx` types
out_options_forward strings.Builder // forward `option_xxxx` types
out_options strings.Builder // `option_xxxx` types
out_results_forward strings.Builder // forward`result_xxxx` types
out_results strings.Builder // `result_xxxx` types
json_forward_decls strings.Builder // json type forward decls
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
@ -101,7 +103,10 @@ mut:
is_fn_index_call bool
is_cc_msvc bool // g.pref.ccompiler == 'msvc'
vlines_path string // set to the proper path for generating #line directives
optionals_pos_forward int // insertion point to forward
options_forward []string // to forward
optionals map[string]string // to avoid duplicates
results_forward []string // to forward
results map[string]string // to avoid duplicates
done_optionals shared []string // to avoid duplicates
chan_pop_optionals map[string]string // types for `x := <-ch or {...}`
@ -270,7 +275,9 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string,
dump_funcs: strings.new_builder(100)
pcs_declarations: strings.new_builder(100)
embedded_data: strings.new_builder(1000)
options: strings.new_builder(100)
out_options_forward: strings.new_builder(100)
out_options: strings.new_builder(100)
out_results_forward: strings.new_builder(100)
out_results: strings.new_builder(100)
shared_types: strings.new_builder(100)
shared_functions: strings.new_builder(100)
@ -456,6 +463,22 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string,
g.write_init_function()
}
// insert for optionals forward
if g.out_options_forward.len > 0 || g.out_results_forward.len > 0 {
tail := g.type_definitions.cut_to(g.optionals_pos_forward)
if g.out_options_forward.len > 0 {
g.type_definitions.writeln('// #start V forward option_xxx definitions:')
g.type_definitions.writeln(g.out_options_forward.str())
g.type_definitions.writeln('// #end V forward option_xxx definitions\n')
}
if g.out_results_forward.len > 0 {
g.type_definitions.writeln('// #start V forward result_xxx definitions:')
g.type_definitions.writeln(g.out_results_forward.str())
g.type_definitions.writeln('// #end V forward result_xxx definitions\n')
}
g.type_definitions.writeln(tail)
}
g.finish()
mut b := strings.new_builder(640000)
@ -488,7 +511,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string,
b.writeln('\n// V shared types:')
b.write_string(g.shared_types.str())
b.writeln('\n// V Option_xxx definitions:')
b.write_string(g.options.str())
b.write_string(g.out_options.str())
b.writeln('\n// V result_xxx definitions:')
b.write_string(g.out_results.str())
b.writeln('\n// V json forward decls:')
@ -591,7 +614,9 @@ fn cgen_process_one_file_cb(p &pool.PoolProcessor, idx int, wid int) &Gen {
pcs_declarations: strings.new_builder(100)
hotcode_definitions: strings.new_builder(100)
embedded_data: strings.new_builder(1000)
options: strings.new_builder(100)
out_options_forward: strings.new_builder(100)
out_options: strings.new_builder(100)
out_results_forward: strings.new_builder(100)
out_results: strings.new_builder(100)
shared_types: strings.new_builder(100)
shared_functions: strings.new_builder(100)
@ -615,6 +640,8 @@ fn cgen_process_one_file_cb(p &pool.PoolProcessor, idx int, wid int) &Gen {
array_sort_fn: global_g.array_sort_fn
waiter_fns: global_g.waiter_fns
threaded_fns: global_g.threaded_fns
options_forward: global_g.options_forward
results_forward: global_g.results_forward
done_optionals: global_g.done_optionals
is_autofree: global_g.pref.autofree
referenced_fns: global_g.referenced_fns
@ -650,7 +677,9 @@ pub fn (mut g Gen) free_builders() {
g.shared_functions.free()
g.channel_definitions.free()
g.thread_definitions.free()
g.options.free()
g.out_options_forward.free()
g.out_options.free()
g.out_results_forward.free()
g.out_results.free()
g.json_forward_decls.free()
g.enum_typedefs.free()
@ -734,6 +763,7 @@ pub fn (mut g Gen) init() {
g.cheaders.writeln('#include <spawn.h>')
}
g.write_builtin_types()
g.optionals_pos_forward = g.type_definitions.len
g.write_typedef_types()
g.write_typeof_functions()
g.write_sorted_types()
@ -1086,7 +1116,11 @@ fn (mut g Gen) write_optionals() {
}
done << base
g.typedefs.writeln('typedef struct $styp $styp;')
g.options.write_string(g.optional_type_text(styp, base) + ';\n\n')
if base in g.options_forward {
g.out_options_forward.write_string(g.optional_type_text(styp, base) + ';\n\n')
} else {
g.out_options.write_string(g.optional_type_text(styp, base) + ';\n\n')
}
}
}
@ -1098,8 +1132,12 @@ fn (mut g Gen) write_results() {
}
done << base
g.typedefs.writeln('typedef struct $styp $styp;')
if base in g.results_forward {
g.out_results_forward.write_string(g.result_type_text(styp, base) + ';\n\n')
} else {
g.out_results.write_string(g.result_type_text(styp, base) + ';\n\n')
}
}
for k, _ in g.table.anon_struct_names {
ck := c_name(k)
g.typedefs.writeln('typedef struct $ck $ck;')
@ -1314,6 +1352,12 @@ pub fn (mut g Gen) write_typedef_types() {
g.type_definitions.writeln(def_str)
} else {
g.type_definitions.writeln('typedef $fixed $styp [$len];')
base := g.typ(info.elem_type.clear_flag(.optional).clear_flag(.result))
if info.elem_type.has_flag(.optional) && base !in g.options_forward {
g.options_forward << base
} else if info.elem_type.has_flag(.result) && base !in g.results_forward {
g.results_forward << base
}
}
}
}
@ -2272,7 +2316,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
return
}
if got_sym.info !is ast.Interface && exp_sym.info is ast.Interface
&& got_type.idx() != expected_type.idx() && !expected_type.has_flag(.optional) {
&& got_type.idx() != expected_type.idx() && !expected_type.has_flag(.optional)
&& !expected_type.has_flag(.result) {
if expr is ast.StructInit && !got_type.is_ptr() {
g.inside_cast_in_heap++
got_styp := g.cc_type(got_type.ref(), true)
@ -2362,7 +2407,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
// Generic dereferencing logic
neither_void := ast.voidptr_type !in [got_type, expected_type]
if expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f)
&& !expected_type.has_flag(.optional) {
&& !expected_type.has_flag(.optional) && !expected_type.has_flag(.result) {
shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*`
if got_type_raw.is_ptr() {
g.error('cannot convert reference to `shared`', expr.pos())
@ -2393,8 +2438,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
got_deref_type := got_type.deref()
deref_sym := g.table.sym(got_deref_type)
deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
got_is_opt := got_type.has_flag(.optional)
if deref_will_match || got_is_opt || expr.is_auto_deref_var() {
got_is_opt_or_res := got_type.has_flag(.optional) || got_type.has_flag(.result)
if deref_will_match || got_is_opt_or_res || expr.is_auto_deref_var() {
g.write('*')
}
}
@ -3452,8 +3497,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
}
sym := g.table.sym(g.unwrap_generic(node.expr_type))
// if node expr is a root ident and an optional
mut is_optional := node.expr is ast.Ident && node.expr_type.has_flag(.optional)
if is_optional {
mut is_opt_or_res := node.expr is ast.Ident
&& (node.expr_type.has_flag(.optional) || node.expr_type.has_flag(.result))
if is_opt_or_res {
opt_base_typ := g.base_type(node.expr_type)
g.writeln('(*($opt_base_typ*)')
}
@ -3584,7 +3630,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
} else {
g.expr(node.expr)
}
if is_optional {
if is_opt_or_res {
g.write('.data)')
}
// struct embedding

View File

@ -0,0 +1,44 @@
struct Foo1 {
arr1 [5]?int
arr2 [5]int
}
fn get_has_optional_fixed() ?int {
foo := Foo1{}
return foo.arr1[0]
}
fn get_no_optional_fixed() int {
foo := Foo1{}
return foo.arr2[0]
}
fn test_optional_fixed() ? {
x := get_has_optional_fixed()?
assert x == 0
assert get_no_optional_fixed() == 0
}
struct Foo2 {
mut:
arr1 []?int
arr2 []int
}
fn get_has_optional() ?int {
mut foo := Foo2{}
foo.arr1 << 0
return foo.arr1[0]
}
fn get_no_optional() int {
mut foo := Foo2{}
foo.arr2 << 0
return foo.arr2[0]
}
fn test_optional_non_fixed() ? {
x := get_has_optional()?
assert x == 0
assert get_no_optional() == 0
}