Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 28 additions & 6 deletions vlib/v/gen/c/str.v
Original file line number Diff line number Diff line change
Expand Up @@ -239,28 +239,50 @@ 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)
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)
if expr.op == .amp && right_type.has_flag(.option) {
Comment on lines +248 to +252

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hoist option-ref lowering before enum stringification

Because this option-reference lowering is added only inside the later auto-string branch, option references whose payload symbol is handled by an earlier branch never use it. For example, with enum Color { red } and val := ?Color(.red), ${&val} enters the sym.kind == .enum path above before this block and still passes the original ?Color storage to the ?&Color stringifier, leaving this fix incomplete for option enum payloads.

Useful? React with 👍 / 👎.

Comment on lines +251 to +252

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve generic right types before detecting option refs

In generic instantiations where T is an option, the prefix expression's right_type can still be the generic parameter while the interpolation expression type has already been resolved for the concrete call. Testing expr.right_type directly leaves option_payload_ref_tmp false for ${&val} inside fn f[T](val T) called as f[?int](...), so it falls back to the old address-of emission instead of the new option-payload temporary; resolve/unwrap the right type before checking the option flag.

Useful? React with 👍 / 👎.

option_payload_ref_tmp = match right_expr {
ast.Ident, ast.IndexExpr, ast.SelectorExpr { true }
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)
Comment on lines +260 to +261

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve option payload aliases when building the temp

When val has type ?Alias and Alias defines its own str(), this rebuilds the interpolation temporary from fully_unaliased_type(expr.right_type), turning the semantic ?&Alias into ?&<parent>. The g.get_str_fn(exp_typ) call below then generates/calls the parent type's stringifier instead of the alias override, so ${&val} silently loses the alias-specific formatting even though plain ${val} preserves it; only the option detection needs unaliasing, while the temporary type should keep the original payload alias when the option flag is already present.

Useful? React with 👍 / 👎.

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())
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,23 @@ fn int_ref_string(n &int) string {
return '${n}'
}

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() {
mut count := 10
count_ref := &count
Expand Down
Loading