Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -13158,6 +13158,11 @@ fn (mut g Gen) interface_table() string {
// That keeps stray bytes from overlapping storage, like unions, from
// aliasing a valid concrete interface variant.
interface_index_name := '_${interface_name}_${cctype}_index'
interface_index_case_name := if g.pref.use_cache {
'${interface_index_name}_enum'
} else {
interface_index_name
}
if already_generated_mwrappers[interface_index_name] > 0 {
continue
}
Expand Down Expand Up @@ -13247,7 +13252,7 @@ static inline __shared__${interface_name} ${shared_fn_name}(__shared__${cctype}*
return ${cast_shared_struct_str};
}')
if shared_interface_mtx_helper_needed {
shared_interface_mtx_cases.writeln('\t\tcase ${interface_index_name}:')
shared_interface_mtx_cases.writeln('\t\tcase ${interface_index_case_name}:')
Comment thread
medvednikov marked this conversation as resolved.
Outdated
shared_interface_mtx_cases.writeln('\t\t\treturn &(((__shared__${cctype}*)((char*)x->val._${cctype} - __offsetof(__shared__${cctype}, val)))->mtx);')
}
}
Expand Down Expand Up @@ -13512,8 +13517,23 @@ return ${cast_shared_struct_str};
}
iin_idx := already_generated_mwrappers[interface_index_name] - iinidx_minimum_base + 1
if g.pref.build_mode != .build_module {
sb.writeln('enum { ${interface_index_name} = ${iin_idx} };')
if g.pref.use_cache {
// With -usecache, modules like `builtin` are compiled separately
// in build_module mode, where the index is emitted as
// `extern const u32 ..._index;` and referenced. The main program
// must therefore provide a real, externally-linked definition
// (not a compile-time `enum` constant, which has no linker
// symbol), otherwise the reference is undefined at link time -
// e.g. `undefined symbol: _IError_None___index` on FreeBSD/clang.
sb.writeln('enum { ${interface_index_case_name} = ${iin_idx} };')
sb.writeln('const u32 ${interface_index_name} = ${interface_index_case_name};')
Comment thread
medvednikov marked this conversation as resolved.
Outdated
} else {
sb.writeln('enum { ${interface_index_name} = ${iin_idx} };')
}
} else {
if g.pref.use_cache {
sb.writeln('enum { ${interface_index_case_name} = ${iin_idx} };')
}
sb.writeln('extern const u32 ${interface_index_name};')
}
}
Expand Down
93 changes: 93 additions & 0 deletions vlib/v/tests/usecache_interface_index_symbol_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import os

const vexe = @VEXE

// Regression test for https://github.com/vlang/v/issues/27330
//
// With -usecache, modules like `builtin` are compiled separately in
// build_module mode, where the interface type-table index is emitted as
// `extern const u32 ..._index;` and referenced. The main program must therefore
// provide a real, externally-linked `const u32 ..._index = N;` definition - a
// compile-time `enum` constant has no linker symbol, so the reference would be
// undefined at link time (e.g. `undefined symbol: _IError_None___index` on
// FreeBSD/clang).
fn test_usecache_interface_index_is_real_symbol() {
tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_27330')
os.mkdir_all(tmp_dir) or { panic(err) }
defer {
os.rmdir_all(tmp_dir) or {}
}
source_path := os.join_path(tmp_dir, 'issue_27330.v')
os.write_file(source_path, "fn main() {\n\tprintln('hello world')\n}\n") or { panic(err) }

// -o - dumps the generated C of the main program to stdout.
res := os.execute('${os.quoted_path(vexe)} -usecache -o - ${os.quoted_path(source_path)}')
if res.exit_code != 0 {
panic(res.output)
}
// The index must be a real (externally-linked) definition, not a bare enum.
// The separate enum keeps an integer constant expression available for C
// contexts like switch case labels.
assert res.output.contains('enum { _IError_None___index_enum =')
assert res.output.contains('const u32 _IError_None___index = _IError_None___index_enum;')
assert !res.output.contains('enum { _IError_None___index =')

// Sanity check: without -usecache the compile-time enum form is kept (it is
// the tcc-friendly form and needs no external symbol in a single build).
res2 := os.execute('${os.quoted_path(vexe)} -o - ${os.quoted_path(source_path)}')
if res2.exit_code != 0 {
panic(res2.output)
}
assert res2.output.contains('enum { _IError_None___index =')
}

fn test_usecache_shared_interface_lock_uses_enum_index_in_case_labels() {
tmp_dir := os.join_path(os.vtmp_dir(), 'v_issue_27330_shared')
os.mkdir_all(tmp_dir) or { panic(err) }
defer {
os.rmdir_all(tmp_dir) or {}
}
source_path := os.join_path(tmp_dir, 'shared_interface.v')
os.write_file(source_path, '
interface MyInterface {
foo() string
}

struct MyStruct {
pub mut:
fooer shared MyInterface
}

struct MyImplementor {
mut:
num int
}

fn (m MyImplementor) foo() string {
return "Hello World!"
}

fn main() {
shared imp := MyImplementor{
num: 1
}
s := MyStruct{
fooer: imp
}
lock s.fooer {
println(s.fooer.foo())
}
}
') or {
panic(err)
}

res := os.execute('${os.quoted_path(vexe)} -usecache -o - ${os.quoted_path(source_path)}')
if res.exit_code != 0 {
panic(res.output)
}
assert res.output.contains('enum { _main__MyInterface_main__MyImplementor_index_enum =')
assert res.output.contains('const u32 _main__MyInterface_main__MyImplementor_index = _main__MyInterface_main__MyImplementor_index_enum;')
assert res.output.contains('case _main__MyInterface_main__MyImplementor_index_enum:')
assert !res.output.contains('case _main__MyInterface_main__MyImplementor_index:')
}
Loading