From b6a80fd4e544921dc201e015cdc7eba775fd6d36 Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Mon, 13 Jul 2026 06:19:16 +0300 Subject: [PATCH 1/3] cgen: fix option reference string interpolation --- vlib/v/gen/c/str.v | 25 +++++++++++++++---- .../string_interpolation_test.v | 9 +++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index 8ea1308f6d7489..973c680314405f 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -250,17 +250,32 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { } else { g.get_str_fn(exp_typ) } - temp_var_needed := expr is ast.CallExpr - && (expr.return_type.is_ptr() || g.table.sym(expr.return_type).is_c_struct()) + option_payload_ref_tmp := if expr is ast.PrefixExpr { + right_expr := expr.right.remove_par() + expr.op == .amp && expr.right_type.has_flag(.option) && match right_expr { + ast.Ident, ast.IndexExpr, ast.SelectorExpr { true } + else { false } + } + } else { + false + } + temp_var_needed := (expr is ast.CallExpr && (expr.return_type.is_ptr() + || g.table.sym(expr.return_type).is_c_struct())) + || option_payload_ref_tmp mut tmp_var := '' if temp_var_needed { tmp_var = g.new_tmp_var() ret_typ := g.styp(exp_typ) line := g.go_before_last_stmt().trim_space() g.empty_line = true - g.write('${ret_typ} ${tmp_var} = ') - g.expr(expr) - g.writeln(';') + if option_payload_ref_tmp && expr is ast.PrefixExpr { + g.writeln('${ret_typ} ${tmp_var};') + g.gen_option_payload_ref(expr, exp_typ, tmp_var) + } else { + g.write('${ret_typ} ${tmp_var} = ') + g.expr(expr) + g.writeln(';') + } g.write(line) } if is_ptr && !is_var_mut { diff --git a/vlib/v/tests/builtin_strings_and_interpolation/string_interpolation_test.v b/vlib/v/tests/builtin_strings_and_interpolation/string_interpolation_test.v index 5f3ecad680659b..cee7bd4f3bfa83 100644 --- a/vlib/v/tests/builtin_strings_and_interpolation/string_interpolation_test.v +++ b/vlib/v/tests/builtin_strings_and_interpolation/string_interpolation_test.v @@ -164,6 +164,15 @@ fn int_ref_string(n &int) string { return '${n}' } +fn option_ref_string(val ?int) string { + return '${&val}' +} + +fn test_string_interpolation_reference_to_option_value() { + assert option_ref_string(?int(42)).contains('Option(') + assert option_ref_string(?int(none)).contains('Option(') +} + fn test_int_ref_string_interpolation() { mut count := 10 count_ref := &count From 79a5bc77f0c11935029aab1b5de3f59638a8f88d Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Wed, 15 Jul 2026 08:20:54 +0300 Subject: [PATCH 2/3] cgen: handle option alias references in interpolation --- vlib/v/gen/c/cgen.v | 3 ++- vlib/v/gen/c/str.v | 20 ++++++++++++------- .../string_interpolation_test.v | 8 ++++++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 6a9b90b8eda219..a3d9b87de6b249 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4292,7 +4292,8 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool { // applicable to situations where the expr_typ does not have `option` and `result`, // e.g. field default: "foo ?int = 1", field assign: "foo = 1", field init: "foo: 1" fn (mut g Gen) gen_option_payload_ref(expr ast.PrefixExpr, ret_typ ast.Type, tmp_var string) bool { - if expr.op != .amp || !expr.right_type.has_flag(.option) { + right_type := g.table.fully_unaliased_type(expr.right_type) + if expr.op != .amp || !right_type.has_flag(.option) { return false } mut right_expr := expr.right diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index 973c680314405f..58274553f3d001 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -239,26 +239,32 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { } } - exp_typ := if unwrap_opt_or_res { typ.clear_option_and_result() } else { typ } + mut exp_typ := if unwrap_opt_or_res { typ.clear_option_and_result() } else { typ } if unwrap_opt_or_res { typ = exp_typ } is_dump_expr := expr is ast.DumpExpr is_var_mut := g.expr_is_auto_deref_var(expr) && !typ.has_flag(.option) - str_fn_name := if mut_arg_option_type != 0 { - g.get_str_fn(mut_arg_option_type) - } else { - g.get_str_fn(exp_typ) - } option_payload_ref_tmp := if expr is ast.PrefixExpr { right_expr := expr.right.remove_par() - expr.op == .amp && expr.right_type.has_flag(.option) && match right_expr { + right_type := g.table.fully_unaliased_type(expr.right_type) + expr.op == .amp && right_type.has_flag(.option) && match right_expr { ast.Ident, ast.IndexExpr, ast.SelectorExpr { true } else { false } } } else { false } + if option_payload_ref_tmp && expr is ast.PrefixExpr { + exp_typ = + g.table.fully_unaliased_type(expr.right_type).clear_option_and_result().ref().set_flag(.option) + typ = exp_typ + } + str_fn_name := if mut_arg_option_type != 0 { + g.get_str_fn(mut_arg_option_type) + } else { + g.get_str_fn(exp_typ) + } temp_var_needed := (expr is ast.CallExpr && (expr.return_type.is_ptr() || g.table.sym(expr.return_type).is_c_struct())) || option_payload_ref_tmp diff --git a/vlib/v/tests/builtin_strings_and_interpolation/string_interpolation_test.v b/vlib/v/tests/builtin_strings_and_interpolation/string_interpolation_test.v index cee7bd4f3bfa83..d35ae1eb796f24 100644 --- a/vlib/v/tests/builtin_strings_and_interpolation/string_interpolation_test.v +++ b/vlib/v/tests/builtin_strings_and_interpolation/string_interpolation_test.v @@ -168,9 +168,17 @@ fn option_ref_string(val ?int) string { return '${&val}' } +type MaybeInt = ?int + +fn option_alias_ref_string(val MaybeInt) string { + return '${&val}' +} + fn test_string_interpolation_reference_to_option_value() { assert option_ref_string(?int(42)).contains('Option(') assert option_ref_string(?int(none)).contains('Option(') + assert option_alias_ref_string(?int(42)).contains('Option(') + assert option_alias_ref_string(?int(none)).contains('Option(') } fn test_int_ref_string_interpolation() { From 878448e1759ccb96a4bbdb0f60a70ee1b4d5f04c Mon Sep 17 00:00:00 2001 From: cyphercodes Date: Wed, 15 Jul 2026 14:26:35 +0300 Subject: [PATCH 3/3] cgen: avoid match in option ref condition --- vlib/v/gen/c/str.v | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index 58274553f3d001..0e13dcdc7208e7 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -245,15 +245,16 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { } is_dump_expr := expr is ast.DumpExpr is_var_mut := g.expr_is_auto_deref_var(expr) && !typ.has_flag(.option) - option_payload_ref_tmp := if expr is ast.PrefixExpr { + mut option_payload_ref_tmp := false + if expr is ast.PrefixExpr { right_expr := expr.right.remove_par() right_type := g.table.fully_unaliased_type(expr.right_type) - expr.op == .amp && right_type.has_flag(.option) && match right_expr { - ast.Ident, ast.IndexExpr, ast.SelectorExpr { true } - else { false } + if expr.op == .amp && right_type.has_flag(.option) { + option_payload_ref_tmp = match right_expr { + ast.Ident, ast.IndexExpr, ast.SelectorExpr { true } + else { false } + } } - } else { - false } if option_payload_ref_tmp && expr is ast.PrefixExpr { exp_typ =