cgen, ast, checker: fix auto deref arg when fn expects ref (#20846)

This commit is contained in:
Felipe Pena 2024-02-16 17:04:34 -03:00 committed by GitHub
parent 1d3147e139
commit d2af0dc96a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 51 additions and 5 deletions

View file

@ -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))

View file

@ -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
}

View file

@ -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)
@ -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 }

View file

@ -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
@ -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))

View file

@ -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 {
@ -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
}
}
}
@ -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 {
@ -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(', ')
}
@ -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
@ -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 {
@ -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(')')
}

View file

@ -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 })
}