Skip to content

Commit

Permalink
cgen, ast, checker: fix auto deref arg when fn expects ref (#20846)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp committed Feb 16, 2024
1 parent 1d3147e commit d2af0dc
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 5 deletions.
1 change: 1 addition & 0 deletions cmd/tools/vast/vast.v
Expand Up @@ -1560,6 +1560,7 @@ fn (t Tree) call_arg(node ast.CallArg) &Node {
obj.add_terse('is_mut', t.bool_node(node.is_mut))
obj.add_terse('share', t.enum_node(node.share))
obj.add_terse('expr', t.expr(node.expr))
obj.add_terse('should_be_ptr', t.bool_node(node.should_be_ptr))
obj.add('is_tmp_autofree', t.bool_node(node.is_tmp_autofree))
obj.add('pos', t.pos(node.pos))
obj.add('comments', t.array_node_comment(node.comments))
Expand Down
1 change: 1 addition & 0 deletions vlib/v/ast/ast.v
Expand Up @@ -803,6 +803,7 @@ pub mut:
typ Type
is_tmp_autofree bool // this tells cgen that a tmp variable has to be used for the arg expression in order to free it after the call
pos token.Pos
should_be_ptr bool // fn expects a ptr for this arg
// tmp_name string // for autofree
}

Expand Down
4 changes: 4 additions & 0 deletions vlib/v/checker/fn.v
Expand Up @@ -1162,6 +1162,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
} else {
func.params[i]
}
// registers if the arg must be passed by ref to disable auto deref args
call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut
if func.is_variadic && call_arg.expr is ast.ArrayDecompose {
if i > func.params.len - 1 {
c.error('too many arguments in call to `${func.name}`', node.pos)
Expand Down Expand Up @@ -1969,6 +1971,8 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
} else {
info.func.params[i]
}
// registers if the arg must be passed by ref to disable auto deref args
arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut
if c.table.sym(param.typ).kind == .interface_ {
// cannot hide interface expected type to make possible to pass its interface type automatically
earg_types << if targ.idx() != param.typ.idx() { param.typ } else { targ }
Expand Down
3 changes: 2 additions & 1 deletion vlib/v/gen/c/cgen.v
Expand Up @@ -212,6 +212,7 @@ mut:
// where an aggregate (at least two types) is generated
// sum type deref needs to know which index to deref because unions take care of the correct field
aggregate_type_idx int
arg_no_auto_deref bool // smartcast must not be dereferenced
branch_parent_pos int // used in BranchStmt (continue/break) for autofree stop position
returned_var_name string // to detect that a var doesn't need to be freed since it's being returned
infix_left_var_name string // a && if expr
Expand Down Expand Up @@ -4695,7 +4696,7 @@ fn (mut g Gen) ident(node ast.Ident) {
}
styp := g.base_type(node.obj.typ)
g.write('*(${styp}*)')
} else {
} else if !g.arg_no_auto_deref {
g.write('*')
}
} else if (g.inside_interface_deref && g.table.is_interface_var(node.obj))
Expand Down
14 changes: 10 additions & 4 deletions vlib/v/gen/c/fn.v
Expand Up @@ -2248,6 +2248,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
if is_variadic && i == expected_types.len - 1 {
break
}
mut is_smartcast := false
if arg.expr is ast.Ident {
if arg.expr.obj is ast.Var {
if arg.expr.obj.smartcasts.len > 0 {
Expand All @@ -2260,6 +2261,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
if cast_sym.info is ast.Aggregate {
expected_types[i] = cast_sym.info.types[g.aggregate_type_idx]
}
is_smartcast = true
}
}
}
Expand Down Expand Up @@ -2295,7 +2297,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
g.write('/*autofree arg*/' + name)
}
} else {
g.ref_or_deref_arg(arg, expected_types[i], node.language)
g.ref_or_deref_arg(arg, expected_types[i], node.language, is_smartcast)
}
} else {
if use_tmp_var_autofree {
Expand Down Expand Up @@ -2366,7 +2368,8 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
noscan := g.check_noscan(arr_info.elem_type)
g.write('new_array_from_c_array${noscan}(${variadic_count}, ${variadic_count}, sizeof(${elem_type}), _MOV((${elem_type}[${variadic_count}]){')
for j in arg_nr .. args.len {
g.ref_or_deref_arg(args[j], arr_info.elem_type, node.language)
g.ref_or_deref_arg(args[j], arr_info.elem_type, node.language,
false)
if j < args.len - 1 {
g.write(', ')
}
Expand All @@ -2393,7 +2396,7 @@ fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int {
expected_type := node.expected_arg_types[i]
typ := g.table.sym(expected_type).cname
g.write('${typ} __tmp_arg_${tmp_cnt_save + i} = ')
g.ref_or_deref_arg(arg, expected_type, node.language)
g.ref_or_deref_arg(arg, expected_type, node.language, false)
g.writeln(';')
}
g.empty_line = false
Expand All @@ -2410,7 +2413,7 @@ fn (mut g Gen) keep_alive_call_postgen(node ast.CallExpr, tmp_cnt_save int) {
}

@[inline]
fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang ast.Language) {
fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang ast.Language, is_smartcast bool) {
arg_typ := if arg.expr is ast.ComptimeSelector {
g.unwrap_generic(g.comptime.get_comptime_var_type(arg.expr))
} else {
Expand Down Expand Up @@ -2525,7 +2528,10 @@ fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang as
}
}
}
// check if the argument must be dereferenced or not
g.arg_no_auto_deref = is_smartcast && !arg_is_ptr && !exp_is_ptr && arg.should_be_ptr
g.expr_with_cast(arg.expr, arg_typ, expected_type)
g.arg_no_auto_deref = false
if needs_closing {
g.write(')')
}
Expand Down
33 changes: 33 additions & 0 deletions vlib/v/tests/sumtype_ptr_arg_test.v
@@ -0,0 +1,33 @@
struct Foo {
foo i32
}

struct Bar {
bar f32
}

type Foobar = Bar | Foo

fn match_sum(sum Foobar) {
match sum {
Foo {
sum_foo(sum)
}
Bar {
sum_bar(sum)
}
}
}

fn sum_foo(i &Foo) {
assert true
}

fn sum_bar(i Bar) {
assert true
}

fn test_main() {
match_sum(Foo{ foo: 5 })
match_sum(Bar{ bar: 5 })
}

0 comments on commit d2af0dc

Please sign in to comment.