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

cgen: fix compile error on locking interface value (#12883)

This commit is contained in:
Toby Webb 2021-12-26 19:01:00 +01:00 committed by GitHub
parent c26e040d33
commit 35418b8413
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 143 additions and 5 deletions

View File

@ -183,6 +183,7 @@ mut:
force_main_console bool // true when [console] used on fn main()
as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast)
obf_table map[string]string
referenced_fns shared map[string]bool // functions that have been referenced
nr_closures int
expected_cast_type ast.Type // for match expr of sumtypes
anon_fn bool
@ -523,6 +524,7 @@ fn cgen_process_one_file_cb(p &pool.PoolProcessor, idx int, wid int) &Gen {
threaded_fns: global_g.threaded_fns
done_optionals: global_g.done_optionals
is_autofree: global_g.pref.autofree
referenced_fns: global_g.referenced_fns
}
g.gen_file()
return g
@ -2353,8 +2355,18 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
'f64' { 'float_literal' }
else { got_styp }
}
exp_styp := exp_sym.cname
mut fname := '/*$exp_sym*/I_${got_styp}_to_Interface_$exp_styp'
got_is_shared := got_type.has_flag(.shared_f)
exp_styp := if got_is_shared { '__shared__$exp_sym.cname' } else { exp_sym.cname }
// If it's shared, we need to use the other caster:
mut fname := if got_is_shared {
'I___shared__${got_styp}_to_shared_Interface_$exp_styp'
} else {
'I_${got_styp}_to_Interface_$exp_styp'
}
lock g.referenced_fns {
g.referenced_fns[fname] = true
}
fname = '/*$exp_sym*/$fname'
if exp_sym.info.is_generic {
fname = g.generic_fn_name(exp_sym.info.concrete_types, fname, false)
}
@ -6497,6 +6509,14 @@ fn (g Gen) as_cast_name_table() string {
return name_ast.str()
}
fn (g Gen) has_been_referenced(fn_name string) bool {
mut referenced := false
lock g.referenced_fns {
referenced = g.referenced_fns[fn_name]
}
return referenced
}
// Generates interface table and interface indexes
fn (mut g Gen) interface_table() string {
mut sb := strings.new_builder(100)
@ -6607,6 +6627,25 @@ fn (mut g Gen) interface_table() string {
static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x) {
return $cast_struct_str;
}')
shared_fn_name := 'I___shared__${cctype}_to_shared_Interface___shared__$interface_name'
// Avoid undefined types errors by only generating the converters that are referenced:
if g.has_been_referenced(shared_fn_name) {
mut cast_shared_struct := strings.new_builder(100)
cast_shared_struct.writeln('(__shared__$interface_name) {')
cast_shared_struct.writeln('\t\t.mtx = {0},')
cast_shared_struct.writeln('\t\t.val = {')
cast_shared_struct.writeln('\t\t\t._$cctype = &x->val,')
cast_shared_struct.writeln('\t\t\t._typ = $interface_index_name,')
cast_shared_struct.writeln('\t\t}')
cast_shared_struct.write_string('\t}')
cast_shared_struct_str := cast_shared_struct.str()
cast_functions.writeln('
// Casting functions for converting "__shared__$cctype" to interface "__shared__$interface_name"
static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x) {
return $cast_shared_struct_str;
}')
}
}
if g.pref.build_mode != .build_module {
@ -6685,7 +6724,13 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype
typ: st.set_nr_muls(1)
}
fargs, _, _ := g.fn_args(params, voidptr(0))
methods_wrapper.write_string(g.out.cut_last(g.out.len - params_start_pos))
mut parameter_name := g.out.cut_last(g.out.len - params_start_pos)
if st.is_ptr() {
parameter_name = parameter_name.trim_left('__shared__')
}
methods_wrapper.write_string(parameter_name)
methods_wrapper.writeln(') {')
methods_wrapper.write_string('\t')
if method.return_type != ast.void_type {
@ -6704,7 +6749,11 @@ static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype
}
methods_wrapper.writeln('${fargs[1..].join(', ')});')
} else {
methods_wrapper.writeln('${method_call}(*${fargs.join(', ')});')
if parameter_name.starts_with('__shared__') {
methods_wrapper.writeln('${method_call}(${fargs.join(', ')}->val);')
} else {
methods_wrapper.writeln('${method_call}(*${fargs.join(', ')});')
}
}
methods_wrapper.writeln('}')
// .speak = Cat_speak_Interface_Animal_method_wrapper

View File

@ -725,9 +725,17 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
$if debug_interface_method_call ? {
eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name | pos: $node.pos')
}
left_is_shared := node.left_type.has_flag(.shared_f)
g.write('${c_name(receiver_type_name)}_name_table[')
g.expr(node.left)
dot := if node.left_type.is_ptr() { '->' } else { '.' }
dot := if left_is_shared {
'->val.'
} else if node.left_type.is_ptr() {
'->'
} else {
'.'
}
mname := c_name(node.name)
g.write('${dot}_typ]._method_${mname}(')
g.expr(node.left)

View File

@ -0,0 +1,81 @@
interface MyInterface {
foo() string
}
struct MyStruct {
pub mut:
fooer shared MyInterface
}
struct MyImplementor {
num int
}
fn (m MyImplementor) foo() string {
// Can read member properties:
num := m.num
return 'Hello World $num!'
}
fn test_shared_interface_lock_1() {
shared imp1 := MyImplementor{
num: 1
}
s1 := MyStruct{
fooer: imp1
}
lock s1.fooer {
assert s1.fooer.foo() == 'Hello World 1!'
}
}
fn test_shared_interface_lock_2() {
shared imp := MyOtherImplementor{
x: 1
y: 2
s: 'testing'
}
s := MyStruct{
fooer: imp
}
lock s.fooer {
assert s.fooer.foo() == 'Hello World (1, 2, testing)!'
}
// Lock is released and can be locked again:
lock s.fooer {
assert s.fooer.foo() == 'Hello World (1, 2, testing)!'
}
}
// TODO: Fix modifying shared interface value
// fn test_shared_interface_can_be_modified() {
// shared imp1 := MyImplementor{num: 6}
// shared imp2 := MyOtherImplementor{
// x: 7
// y: 3
// s: 'here be dragons...'
// }
// s := MyStruct{
// fooer: imp1
// }
// lock s.fooer {
// assert(s.fooer.foo() == 'Hello World 6!')
// s.fooer = imp2
// }
// lock s.fooer {
// assert(s.fooer.foo() == 'Hello World (7, 3, here be dragons...)!')
// }
// }
struct MyOtherImplementor {
x int
y int
s string
}
fn (m MyOtherImplementor) foo() string {
// Different implementation:
return 'Hello World ($m.x, $m.y, $m.s)!'
}