From 78c5946200278130882d01ad7e35b0e23a6c7f9c Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 31 Jan 2026 20:49:21 +0300 Subject: [PATCH] cgen: fix uninitialized interface bug (closes #17372) --- vlib/v/gen/c/auto_str_methods.v | 1 + .../interface_uninitialized_field_str_test.v | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 vlib/v/tests/interfaces/interface_uninitialized_field_str_test.v diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index 6fd1585d5..166ed0008 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -400,6 +400,7 @@ fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, typ_str st mut fn_builder := strings.new_builder(512) clean_interface_v_type_name := util.strip_main_name(typ_str) fn_builder.writeln('${g.static_non_parallel}string indent_${str_fn_name}(${styp} x, ${ast.int_type_name} indent_count) { /* gen_str_for_interface */') + fn_builder.writeln('\tif (x._typ == 0 && x._object == NULL) return _S("nil");') for typ in info.types { sub_sym := g.table.sym(ast.mktyp(typ)) if g.pref.skip_unused && sub_sym.idx !in g.table.used_features.used_syms { diff --git a/vlib/v/tests/interfaces/interface_uninitialized_field_str_test.v b/vlib/v/tests/interfaces/interface_uninitialized_field_str_test.v new file mode 100644 index 000000000..73a98ecd0 --- /dev/null +++ b/vlib/v/tests/interfaces/interface_uninitialized_field_str_test.v @@ -0,0 +1,32 @@ +// Test for bug #17372: Uninitialized interface type field gives runtime error when accessing it +// Expected behavior: print "nil" instead of causing a runtime crash + +interface Boo {} + +struct Foo { + boo Boo +} + +@[noinit] +struct Bar { + boo Boo +} + +fn test_uninitialized_interface_field_str() { + // Printing uninitialized interface field should return "nil", not crash + s := '${Foo{}.boo}' + assert s == 'nil', 'Expected "nil" for uninitialized interface, got "${s}"' +} + +fn test_uninitialized_interface_field_in_struct_str() { + // Struct containing uninitialized interface should also work + foo := Foo{} + s := '${foo}' + assert s.contains('boo: nil'), 'Expected struct str to contain "boo: nil", got "${s}"' +} + +fn test_noinit_struct_with_interface_field() { + bar := Bar{} + s := '${bar.boo}' + assert s == 'nil', 'Expected "nil" for noinit struct interface field, got "${s}"' +} -- 2.39.5