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
26 changes: 20 additions & 6 deletions vlib/v3/gen/c/array.v
Original file line number Diff line number Diff line change
Expand Up @@ -529,17 +529,24 @@ fn (mut g FlatGen) gen_slice_expr(node flat.Node, base_id flat.NodeId, base_type
fn (mut g FlatGen) gen_array_method_call(node flat.Node, fn_node &flat.Node, arr types.Array) {
base_id := g.a.child(fn_node, 0)
mut elem_type := arr.elem_type
receiver_type := types.unwrap_pointer(g.usable_expr_type(base_id))
base_expr_type := g.usable_expr_type(base_id)
receiver_type0 := if g.a.nodes[int(base_id)].kind == .call {
declared := g.declared_call_return_type(base_id)
if declared !is types.Unknown && declared !is types.Void {
declared
} else {
base_expr_type
}
} else {
base_expr_type
}
receiver_type := types.unwrap_pointer(receiver_type0)
if receiver_arr := array_like_type(receiver_type) {
elem_type = receiver_arr.elem_type
}
c_elem := g.value_c_type(elem_type)
base_node := g.a.nodes[int(base_id)]
is_ptr := if base_node.kind == .ident {
g.usable_expr_type(base_id) is types.Pointer
} else {
false
}
is_ptr := receiver_type0 is types.Pointer
dot := if is_ptr { '->' } else { '.' }
match fn_node.value {
'clone' {
Expand Down Expand Up @@ -1743,6 +1750,9 @@ fn (mut g FlatGen) gen_index_assign(node flat.Node) {
if base_type is types.Pointer {
ptr_type := base_type
mut expected_type := ptr_type.base_type
base_node := g.a.node(base_id)
explicit_mut_pointer_param := base_node.kind == .ident
&& g.current_param_is_mut_pointer(base_node.value)
if fixed := array_fixed_type(ptr_type.base_type) {
g.write('(*')
g.gen_expr(base_id)
Expand All @@ -1752,6 +1762,10 @@ fn (mut g FlatGen) gen_index_assign(node flat.Node) {
g.write('((u8*)')
g.gen_expr(base_id)
g.write(')')
} else if explicit_mut_pointer_param {
g.write('(*')
g.gen_mut_pointer_slot_expr(base_id)
g.write(')')
} else {
g.write('(')
g.gen_expr(base_id)
Expand Down
111 changes: 104 additions & 7 deletions vlib/v3/gen/c/cleanc.v
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import v3.types
import v3.util

const spread_index_expected_type_marker = '__v3_spread_index_expected_type'
const source_mut_pointer_deref_marker = '__v3_source_mut_pointer_deref'
const c_inline_header_size_limit = 262_144
const v1_c_headers_source = $embed_file('../../../v/gen/c/cheaders.v').to_string()
const c_objective_c_bridge_qualifiers = ['__bridge', '__bridge_retained', '__bridge_transfer']
Expand Down Expand Up @@ -398,6 +399,7 @@ mut:
cur_param_types map[string]types.Type
cur_concrete_optional_params map[string]bool
cur_mut_params map[string]bool
cur_mut_pointer_params map[string]bool
cur_mut_param_owners map[string]types.ScopeBindingOwner
cur_fn_ret types.Type = types.Type(types.void_)
cur_fn_ret_is_optional bool
Expand Down Expand Up @@ -1016,6 +1018,7 @@ pub fn FlatGen.new() FlatGen {
cur_param_types: map[string]types.Type{}
cur_concrete_optional_params: map[string]bool{}
cur_mut_params: map[string]bool{}
cur_mut_pointer_params: map[string]bool{}
cur_mut_param_owners: map[string]types.ScopeBindingOwner{}
active_locks: []ActiveLock{}
conditional_branch_scopes: []&types.Scope{}
Expand Down Expand Up @@ -2102,6 +2105,7 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
g.cur_param_types.clear()
g.cur_concrete_optional_params.clear()
g.cur_mut_params.clear()
g.cur_mut_pointer_params.clear()
g.cur_mut_param_owners.clear()
g.active_locks = []ActiveLock{}
g.loop_depth = 0
Expand Down Expand Up @@ -9433,6 +9437,44 @@ fn (mut g FlatGen) expr_to_string_with_expected_type(id flat.NodeId, expected ty
return result
}

fn (mut g FlatGen) gen_mut_pointer_slot_expr(id flat.NodeId) {
node := g.a.nodes[int(id)]
if node.kind == .ident && g.current_param_is_mut_pointer(node.value) {
g.write(g.local_decl_cname(node.value))
return
}
g.gen_expr(id)
}

fn (g &FlatGen) source_mut_pointer_param_deref_type(id flat.NodeId) ?types.Type {
if int(id) < 0 || int(id) >= g.a.nodes.len {
return none
}
node := g.a.node(id)
if node.kind in [.expr_stmt, .paren] && node.children_count > 0 {
return g.source_mut_pointer_param_deref_type(g.a.child(node, 0))
}
if node.kind == .block && node.children_count > 0 {
return g.source_mut_pointer_param_deref_type(g.a.child(node, node.children_count - 1))
}
if node.kind == .prefix && node.op == .mul && node.value.len == 0 && node.children_count > 0 {
return g.source_mut_pointer_param_deref_type(g.a.child(node, 0))
}
if node.kind != .prefix || node.op != .mul || node.value != source_mut_pointer_deref_marker
|| node.children_count == 0 {
return none
}
child := g.a.child_node(node, 0)
if child.kind != .ident || !g.current_param_is_mut_pointer(child.value) {
return none
}
slot_type := g.current_param_type(child.value) or { return none }
if slot_type is types.Pointer && slot_type.base_type is types.Pointer {
return slot_type.base_type.base_type
}
return none
}

fn (mut g FlatGen) default_value_to_string(typ types.Type) string {
orig := g.sb
orig_line_start := g.line_start
Expand Down Expand Up @@ -9625,7 +9667,11 @@ fn (mut g FlatGen) gen_cast_from_mut_param_address(id flat.NodeId, ct string) bo
return false
}
g.write('(${ct})(')
g.gen_expr(child_id)
if g.current_param_is_mut_pointer(child.value) {
g.gen_mut_pointer_slot_expr(child_id)
} else {
g.gen_expr(child_id)
}
g.write(')')
return true
}
Expand Down Expand Up @@ -9830,7 +9876,11 @@ fn (mut g FlatGen) gen_current_mut_param_value_read(id flat.NodeId, expected typ
return false
}
g.write('*')
g.gen_expr(id)
if g.current_param_is_mut_pointer(node.value) {
g.gen_mut_pointer_slot_expr(id)
} else {
g.gen_expr(id)
}
return true
}

Expand Down Expand Up @@ -9913,6 +9963,9 @@ fn (mut g FlatGen) gen_expr_with_expected_type(id flat.NodeId, expected types.Ty
}
}
mut actual := if has_known_actual { known_actual } else { g.usable_expr_type(id) }
if deref_type := g.source_mut_pointer_param_deref_type(id) {
actual = deref_type
}
if node.kind == .ident {
if local_type := g.local_ident_type(node.value) {
actual = local_type
Expand Down Expand Up @@ -10946,16 +10999,26 @@ fn (mut g FlatGen) gen_pointer_pointer_struct_selector(base_id flat.NodeId, base
return false
}
struct_name := (struct_type as types.Struct).name
base := g.a.nodes[int(base_id)]
base_is_mut_pointer_param := base.kind == .ident && g.current_param_is_mut_pointer(base.value)

if _ := g.struct_field_type(struct_name, field) {
g.write('(*(')
g.gen_expr(base_id)
if base_is_mut_pointer_param {
g.gen_mut_pointer_slot_expr(base_id)
} else {
g.gen_expr(base_id)
}
g.write('))->${g.cname(field)}')
return true
}
if embedded_path := g.embedded_field_path_for_promoted_selector(inner_base, field) {
g.write('(*(')
g.gen_expr(base_id)
if base_is_mut_pointer_param {
g.gen_mut_pointer_slot_expr(base_id)
} else {
g.gen_expr(base_id)
}
g.write('))')
for embedded in embedded_path {
g.write('->${g.cname(embedded.name)}')
Expand Down Expand Up @@ -12814,6 +12877,15 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
}
}
.ident {
if g.current_param_is_mut_pointer(node.value) {
// A `mut p &T` parameter is stored as a `T**` slot; its value in an
// expression is the `&T` pointer `*p`. Slot/lvalue consumers emit the
// raw parameter name through gen_mut_pointer_slot_expr instead.
g.write('(*')
g.gen_mut_pointer_slot_expr(id)
g.write(')')
return
}
if c_fn_name := g.test_user_main_fn_value_c_name(id, node) {
g.write(c_fn_name)
return
Expand Down Expand Up @@ -13103,6 +13175,11 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
.prefix {
child_id := g.a.child(node, 0)
child := g.a.nodes[int(child_id)]
if node.op == .mul && node.value.len == 0
&& g.source_mut_pointer_param_deref_type(child_id) != none {
g.gen_expr(child_id)
return
}
if node.value == 'shared' {
g.gen_expr(child_id)
return
Expand All @@ -13128,6 +13205,18 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
return
}
if node.op == .mul && child.kind == .ident {
if g.current_param_is_mut_pointer(child.value) {
// A source `*item` must dereference both the `T**` ABI slot and its
// semantic `&T` value. Synthetic dereferences only read the slot.
g.write('(*')
if g.source_mut_pointer_param_deref_type(id) != none {
g.gen_expr(child_id)
} else {
g.gen_mut_pointer_slot_expr(child_id)
}
g.write(')')
Comment on lines +13208 to +13217

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 both dereferences for mut pointer parameters

When an explicit mutable pointer parameter is dereferenced as a value, such as fn copy(mut p &Item) Item { return *p }, the parameter has Item** ABI storage: one dereference reads the semantic &Item value and the source * requires a second dereference to produce Item. This branch instead emits only (*p), yielding Item* and causing valid direct returns, assignments, and (*p)++ expressions to generate invalid or incorrect C; the existing selector test masks this because the subsequent ->field performs the missing dereference. Preserve the source dereference here and cover the standalone path in the required C-output validation.

AGENTS.md reference: AGENTS.md:L509-L513

Useful? React with 👍 / 👎.

return
}
if typ := g.current_param_type(child.value) {
if typ !is types.Pointer {
g.gen_expr(child_id)
Expand Down Expand Up @@ -13347,7 +13436,11 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
}
if child.kind == .ident && g.current_param_is_mut(child.value) {
g.write('(*')
g.gen_expr(child_id)
if g.current_param_is_mut_pointer(child.value) {
g.gen_mut_pointer_slot_expr(child_id)
} else {
g.gen_expr(child_id)
}
g.write(')')
} else {
g.gen_expr(child_id)
Expand All @@ -13372,7 +13465,11 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
return
}
}
base_type0 := g.usable_expr_type(base_id)
mut base_type0 := g.usable_expr_type(base_id)
base_is_source_mut_pointer_deref := g.source_mut_pointer_param_deref_type(base_id) != none
if deref_type := g.source_mut_pointer_param_deref_type(base_id) {
base_type0 = deref_type
}
base_type_clean := types.unwrap_pointer(base_type0)
if base_type0 is types.Channel && node.value in ['closed', 'len', 'cap'] {
if node.value == 'closed' {
Expand Down Expand Up @@ -13683,7 +13780,7 @@ fn (mut g FlatGen) gen_expr(id flat.NodeId) {
g.write(')')
}
mut is_ptr := false
mut local_type_known := false
mut local_type_known := base_is_source_mut_pointer_deref
if base.kind == .ident {
if typ := g.tc.cur_scope.lookup(base.value) {
local_type_known = true
Expand Down
Loading
Loading