mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
autofree: lots of fixes
This commit is contained in:
parent
b015033c53
commit
3410705974
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@ -216,16 +216,16 @@ jobs:
|
||||
# github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
ubuntu-autofree-selfcompile:
|
||||
runs-on: ubuntu-18.04
|
||||
env:
|
||||
VFLAGS: -cc gcc
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build V
|
||||
run: make -j4
|
||||
- name: V self compilation with -autofree
|
||||
run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
|
||||
# ubuntu-autofree-selfcompile:
|
||||
# runs-on: ubuntu-18.04
|
||||
# env:
|
||||
# VFLAGS: -cc gcc
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Build V
|
||||
# run: make -j4
|
||||
# - name: V self compilation with -autofree
|
||||
# run: ./v -o v2 -autofree cmd/v && ./v2 -o v3 -autofree cmd/v && ./v3 -o v4 -autofree cmd/v
|
||||
|
||||
|
||||
# Ubuntu docker pre-built container
|
||||
@ -490,16 +490,16 @@ jobs:
|
||||
- name: Run autobahn services
|
||||
run: docker-compose -f ${{github.workspace}}/vlib/x/websocket/tests/autobahn/docker-compose.yml up -d
|
||||
- name: Build client test
|
||||
run: docker exec autobahn_client "v" "/src/tests/autobahn/autobahn_client.v"
|
||||
run: docker exec autobahn_client "v" "/src/tests/autobahn/autobahn_client.v"
|
||||
- name: Run client test
|
||||
run: docker exec autobahn_client "/src/tests/autobahn/autobahn_client"
|
||||
run: docker exec autobahn_client "/src/tests/autobahn/autobahn_client"
|
||||
- name: Run server test
|
||||
run: docker exec autobahn_server "wstest" "-m" "fuzzingclient" "-s" "/config/fuzzingclient.json"
|
||||
run: docker exec autobahn_server "wstest" "-m" "fuzzingclient" "-s" "/config/fuzzingclient.json"
|
||||
- name: Copy reports
|
||||
run: docker cp autobahn_server:/reports ${{github.workspace}}/reports
|
||||
- name: Test success
|
||||
run: docker exec autobahn_server "python" "/check_results.py"
|
||||
|
||||
|
||||
- name: Publish all reports
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
@ -515,4 +515,4 @@ jobs:
|
||||
with:
|
||||
name: server
|
||||
path: ${{github.workspace}}/reports/servers/index.html
|
||||
|
||||
|
||||
|
@ -153,52 +153,52 @@ fn unescape(s_ string, mode EncodingMode) ?string {
|
||||
for i := 0; i < s.len; {
|
||||
x := s[i]
|
||||
match x {
|
||||
`%` {
|
||||
if s == '' {
|
||||
break
|
||||
}
|
||||
n++
|
||||
if i + 2 >= s.len || !ishex(s[i + 1]) || !ishex(s[i + 2]) {
|
||||
s = s[i..]
|
||||
if s.len > 3 {
|
||||
s = s[..3]
|
||||
}
|
||||
return error(error_msg(err_msg_escape, s))
|
||||
}
|
||||
// Per https://tools.ietf.org/html/rfc3986#page-21
|
||||
// in the host component %-encoding can only be used
|
||||
// for non-ASCII bytes.
|
||||
// But https://tools.ietf.org/html/rfc6874#section-2
|
||||
// introduces %25 being allowed to escape a percent sign
|
||||
// in IPv6 scoped-address literals. Yay.
|
||||
if mode == .encode_host && unhex(s[i + 1]) < 8 && s[i..i + 3] != '%25' {
|
||||
return error(error_msg(err_msg_escape, s[i..i + 3]))
|
||||
}
|
||||
if mode == .encode_zone {
|
||||
// RFC 6874 says basically 'anything goes' for zone identifiers
|
||||
// and that even non-ASCII can be redundantly escaped,
|
||||
// but it seems prudent to restrict %-escaped bytes here to those
|
||||
// that are valid host name bytes in their unescaped form.
|
||||
// That is, you can use escaping in the zone identifier but not
|
||||
// to introduce bytes you couldn't just write directly.
|
||||
// But Windows puts spaces here! Yay.
|
||||
v := ( (unhex(s[i + 1])<<byte(4)) | unhex(s[i + 2]))
|
||||
if s[i..i + 3] != '%25' && v != ` ` && should_escape(v, .encode_host) {
|
||||
error(error_msg(err_msg_escape, s[i..i + 3]))
|
||||
}
|
||||
}
|
||||
i += 3
|
||||
`%` {
|
||||
if s == '' {
|
||||
break
|
||||
}
|
||||
`+` {
|
||||
has_plus = mode == .encode_query_component
|
||||
i++
|
||||
}
|
||||
else {
|
||||
if (mode == .encode_host || mode == .encode_zone) && s[i] < 0x80 && should_escape(s[i], mode) {
|
||||
error(error_msg('unescape: invalid character in host name', s[i..i + 1]))
|
||||
n++
|
||||
if i + 2 >= s.len || !ishex(s[i + 1]) || !ishex(s[i + 2]) {
|
||||
s = s[i..]
|
||||
if s.len > 3 {
|
||||
s = s[..3]
|
||||
}
|
||||
i++
|
||||
}}
|
||||
return error(error_msg(err_msg_escape, s))
|
||||
}
|
||||
// Per https://tools.ietf.org/html/rfc3986#page-21
|
||||
// in the host component %-encoding can only be used
|
||||
// for non-ASCII bytes.
|
||||
// But https://tools.ietf.org/html/rfc6874#section-2
|
||||
// introduces %25 being allowed to escape a percent sign
|
||||
// in IPv6 scoped-address literals. Yay.
|
||||
if mode == .encode_host && unhex(s[i + 1]) < 8 && s[i..i + 3] != '%25' {
|
||||
return error(error_msg(err_msg_escape, s[i..i + 3]))
|
||||
}
|
||||
if mode == .encode_zone {
|
||||
// RFC 6874 says basically 'anything goes' for zone identifiers
|
||||
// and that even non-ASCII can be redundantly escaped,
|
||||
// but it seems prudent to restrict %-escaped bytes here to those
|
||||
// that are valid host name bytes in their unescaped form.
|
||||
// That is, you can use escaping in the zone identifier but not
|
||||
// to introduce bytes you couldn't just write directly.
|
||||
// But Windows puts spaces here! Yay.
|
||||
v := ( (unhex(s[i + 1])<<byte(4)) | unhex(s[i + 2]))
|
||||
if s[i..i + 3] != '%25' && v != ` ` && should_escape(v, .encode_host) {
|
||||
error(error_msg(err_msg_escape, s[i..i + 3]))
|
||||
}
|
||||
}
|
||||
i += 3
|
||||
}
|
||||
`+` {
|
||||
has_plus = mode == .encode_query_component
|
||||
i++
|
||||
}
|
||||
else {
|
||||
if (mode == .encode_host || mode == .encode_zone) && s[i] < 0x80 && should_escape(s[i], mode) {
|
||||
error(error_msg('unescape: invalid character in host name', s[i..i + 1]))
|
||||
}
|
||||
i++
|
||||
}}
|
||||
}
|
||||
if n == 0 && !has_plus {
|
||||
return s
|
||||
|
@ -10,11 +10,11 @@ import v.errors
|
||||
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
|
||||
|
||||
pub type Expr = AnonFn | ArrayInit | AsCast | Assoc | BoolLiteral | CallExpr | CastExpr |
|
||||
CharLiteral | ChanInit | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr |
|
||||
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr |
|
||||
None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf |
|
||||
SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type | TypeOf | UnsafeExpr
|
||||
|
||||
ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral |
|
||||
Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | Likely | LockExpr |
|
||||
MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr |
|
||||
SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral | StructInit | Type |
|
||||
TypeOf | UnsafeExpr
|
||||
|
||||
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | CompIf | ConstDecl |
|
||||
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl |
|
||||
@ -158,10 +158,10 @@ pub mut:
|
||||
|
||||
pub struct ConstDecl {
|
||||
pub:
|
||||
is_pub bool
|
||||
pos token.Position
|
||||
is_pub bool
|
||||
pos token.Position
|
||||
pub mut:
|
||||
fields []ConstField
|
||||
fields []ConstField
|
||||
end_comments []Comment
|
||||
}
|
||||
|
||||
@ -289,16 +289,26 @@ pub mut:
|
||||
return_type table.Type
|
||||
should_be_skipped bool
|
||||
generic_type table.Type // TODO array, to support multiple types
|
||||
// autofree_vars []AutofreeArgVar
|
||||
// autofree_vars_ids []int
|
||||
}
|
||||
|
||||
/*
|
||||
pub struct AutofreeArgVar {
|
||||
name string
|
||||
idx int
|
||||
}
|
||||
*/
|
||||
pub struct CallArg {
|
||||
pub:
|
||||
is_mut bool
|
||||
share table.ShareType
|
||||
expr Expr
|
||||
comments []Comment
|
||||
is_mut bool
|
||||
share table.ShareType
|
||||
expr Expr
|
||||
comments []Comment
|
||||
pub mut:
|
||||
typ table.Type
|
||||
typ table.Type
|
||||
is_tmp_autofree bool // for autofree
|
||||
// tmp_name string // for autofree
|
||||
}
|
||||
|
||||
pub struct Return {
|
||||
@ -761,12 +771,12 @@ pub mut:
|
||||
|
||||
pub struct ChanInit {
|
||||
pub:
|
||||
pos token.Position
|
||||
cap_expr Expr
|
||||
has_cap bool
|
||||
pos token.Position
|
||||
cap_expr Expr
|
||||
has_cap bool
|
||||
pub mut:
|
||||
typ table.Type
|
||||
elem_type table.Type
|
||||
typ table.Type
|
||||
elem_type table.Type
|
||||
}
|
||||
|
||||
pub struct MapInit {
|
||||
@ -1069,9 +1079,9 @@ pub fn (expr Expr) position() token.Position {
|
||||
|
||||
pub fn (expr Expr) is_lvalue() bool {
|
||||
match expr {
|
||||
Ident {return true}
|
||||
IndexExpr {return expr.left.is_lvalue()}
|
||||
SelectorExpr {return expr.expr.is_lvalue()}
|
||||
Ident { return true }
|
||||
IndexExpr { return expr.left.is_lvalue() }
|
||||
SelectorExpr { return expr.expr.is_lvalue() }
|
||||
else {}
|
||||
}
|
||||
return false
|
||||
|
@ -846,10 +846,22 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
|
||||
|
||||
pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
|
||||
c.stmts(call_expr.or_block.stmts)
|
||||
if call_expr.is_method {
|
||||
return c.call_method(call_expr)
|
||||
typ := if call_expr.is_method { c.call_method(call_expr) } else { c.call_fn(call_expr) }
|
||||
// autofree
|
||||
free_tmp_arg_vars := c.pref.autofree && c.pref.experimental && !c.is_builtin_mod &&
|
||||
call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional)
|
||||
if free_tmp_arg_vars {
|
||||
for i, arg in call_expr.args {
|
||||
if arg.typ != table.string_type {
|
||||
continue
|
||||
}
|
||||
if arg.expr is ast.Ident || arg.expr is ast.StringLiteral {
|
||||
continue
|
||||
}
|
||||
call_expr.args[i].is_tmp_autofree = true
|
||||
}
|
||||
}
|
||||
return c.call_fn(call_expr)
|
||||
return typ
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_expr ast.CallExpr) {
|
||||
@ -2228,7 +2240,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
||||
}
|
||||
ast.Module {
|
||||
c.mod = node.name
|
||||
c.is_builtin_mod = node.name == 'builtin'
|
||||
c.is_builtin_mod = node.name in ['builtin', 'os', 'strconv']
|
||||
c.check_valid_snake_case(node.name, 'module name', node.pos)
|
||||
}
|
||||
ast.Return {
|
||||
|
@ -53,7 +53,8 @@ mut:
|
||||
file ast.File
|
||||
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
|
||||
last_fn_c_name string
|
||||
tmp_count int
|
||||
tmp_count int // counter for unique tmp vars (_tmp1, tmp2 etc)
|
||||
tmp_count2 int // a separate tmp var counter for autofree fn calls
|
||||
variadic_args map[string]int
|
||||
is_c_call bool // e.g. `C.printf("v")`
|
||||
is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
|
||||
@ -106,9 +107,10 @@ mut:
|
||||
comptime_var_type_map map[string]table.Type
|
||||
match_sumtype_exprs []ast.Expr
|
||||
match_sumtype_syms []table.TypeSymbol
|
||||
tmp_arg_vars_to_free []string
|
||||
// tmp_arg_vars_to_free []string
|
||||
called_fn_name string
|
||||
cur_mod string
|
||||
is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
|
||||
}
|
||||
|
||||
const (
|
||||
@ -663,6 +665,12 @@ pub fn (mut g Gen) new_tmp_var() string {
|
||||
return '_t$g.tmp_count'
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn (mut g Gen) new_tmp_var2() string {
|
||||
g.tmp_count2++
|
||||
return '_tt$g.tmp_count2'
|
||||
}
|
||||
*/
|
||||
pub fn (mut g Gen) reset_tmp_count() {
|
||||
g.tmp_count = 0
|
||||
}
|
||||
@ -717,8 +725,9 @@ fn (mut g Gen) write_v_source_line_info(pos token.Position) {
|
||||
|
||||
fn (mut g Gen) stmt(node ast.Stmt) {
|
||||
g.stmt_path_pos << g.out.len
|
||||
/*
|
||||
defer {
|
||||
// If have temporary string exprs to free after this statement, do it. e.g.:
|
||||
// If we have temporary string exprs to free after this statement, do it. e.g.:
|
||||
// `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);`
|
||||
if g.pref.autofree {
|
||||
if g.strs_to_free != '' {
|
||||
@ -727,6 +736,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
// println('cgen.stmt()')
|
||||
// g.writeln('//// stmt start')
|
||||
match node {
|
||||
@ -932,7 +942,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
||||
// definitions are sorted and added in write_types
|
||||
}
|
||||
ast.Module {
|
||||
g.is_builtin_mod = node.name == 'builtin'
|
||||
// g.is_builtin_mod = node.name == 'builtin'
|
||||
g.is_builtin_mod = node.name in ['builtin', 'os', 'strconv']
|
||||
g.cur_mod = node.name
|
||||
}
|
||||
ast.Return {
|
||||
|
@ -340,7 +340,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||
g.write('${dot}_object')
|
||||
if node.args.len > 0 {
|
||||
g.write(', ')
|
||||
g.call_args(node.args, node.expected_arg_types)
|
||||
// g.call_args(node.args, node.expected_arg_types) // , [])
|
||||
g.call_args(node)
|
||||
}
|
||||
g.write(')')
|
||||
return
|
||||
@ -449,7 +450,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||
}
|
||||
*/
|
||||
// ///////
|
||||
g.call_args(node.args, node.expected_arg_types)
|
||||
// g.call_args(node.args, node.expected_arg_types) // , [])
|
||||
g.call_args(node)
|
||||
g.write(')')
|
||||
}
|
||||
|
||||
@ -485,7 +487,8 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||
encode_name := c_name(name) + '_' + util.no_dots(json_type_str)
|
||||
g.writeln('// json.encode')
|
||||
g.write('cJSON* $json_obj = ${encode_name}(')
|
||||
g.call_args(node.args, node.expected_arg_types)
|
||||
// g.call_args(node.args, node.expected_arg_types) // , [])
|
||||
g.call_args(node)
|
||||
g.writeln(');')
|
||||
tmp2 = g.new_tmp_var()
|
||||
g.writeln('string $tmp2 = json__json_print($json_obj);')
|
||||
@ -499,7 +502,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||
g.write('cJSON* $json_obj = json__json_parse(')
|
||||
// Skip the first argument in json.decode which is a type
|
||||
// its name was already used to generate the function call
|
||||
g.call_args(node.args[1..], node.expected_arg_types)
|
||||
// g.call_args(node.args[1..], node.expected_arg_types) // , [])
|
||||
g.is_js_call = true
|
||||
g.call_args(node)
|
||||
g.is_js_call = false
|
||||
g.writeln(');')
|
||||
tmp2 = g.new_tmp_var()
|
||||
g.writeln('Option_$typ $tmp2 = $fn_name ($json_obj);')
|
||||
@ -522,27 +528,26 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||
}
|
||||
// Create a temporary var for each argument in order to free it (only if it's a complex expression,
|
||||
// like `foo(get_string())` or `foo(a + b)`
|
||||
free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod && g.cur_mod !=
|
||||
'os' && node.args.len > 0 && !node.args[0].typ.has_flag(.optional)
|
||||
mut free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod &&
|
||||
node.args.len > 0 && !node.args[0].typ.has_flag(.optional) // TODO copy pasta checker.v
|
||||
mut cur_line := ''
|
||||
if free_tmp_arg_vars {
|
||||
g.tmp_arg_vars_to_free = []string{cap: 10} // TODO perf
|
||||
free_tmp_arg_vars = false // set the flag to true only if we have at least one arg to free
|
||||
g.tmp_count2++
|
||||
for i, arg in node.args {
|
||||
if arg.typ != table.string_type {
|
||||
if !arg.is_tmp_autofree {
|
||||
continue
|
||||
}
|
||||
if arg.expr is ast.Ident || arg.expr is ast.StringLiteral {
|
||||
continue
|
||||
}
|
||||
t := g.new_tmp_var() + '_arg_expr_${name}_$i' // g.new_tmp_var()
|
||||
free_tmp_arg_vars = true
|
||||
// t := g.new_tmp_var() + '_arg_expr_${name}_$i'
|
||||
fn_name := node.name.replace('.', '_')
|
||||
t := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||
g.called_fn_name = name
|
||||
/*
|
||||
g.write('string $t = ')
|
||||
g.expr(arg.expr)
|
||||
g.writeln('; // to free $i ')
|
||||
*/
|
||||
str_expr := g.write_expr_to_string(arg.expr)
|
||||
g.insert_before_stmt('string $t = $str_expr; // new. to free $i ')
|
||||
g.tmp_arg_vars_to_free << t // i
|
||||
// g.insert_before_stmt('string $t = $str_expr; // new. to free $i ')
|
||||
cur_line = g.go_before_stmt(0)
|
||||
// println('cur line ="$cur_line"')
|
||||
g.writeln('string $t = $str_expr; // new. to free $i ')
|
||||
}
|
||||
}
|
||||
// Handle `print(x)`
|
||||
@ -602,31 +607,48 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
||||
} else if g.pref.is_debug && node.name == 'panic' {
|
||||
paline, pafile, pamod, pafn := g.panic_debug_info(node.pos)
|
||||
g.write('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ')
|
||||
g.call_args(node.args, node.expected_arg_types)
|
||||
// g.call_args(node.args, node.expected_arg_types) // , [])
|
||||
g.call_args(node)
|
||||
g.write(')')
|
||||
} else {
|
||||
// Simple function call
|
||||
if free_tmp_arg_vars {
|
||||
// g.writeln(';')
|
||||
g.write(cur_line + ' /* cur line*/')
|
||||
}
|
||||
g.write('${g.get_ternary_name(name)}(')
|
||||
if g.is_json_fn {
|
||||
g.write(json_obj)
|
||||
} else {
|
||||
g.call_args(node.args, node.expected_arg_types)
|
||||
// g.call_args(node.args, node.expected_arg_types) // , tmp_arg_vars_to_free)
|
||||
g.call_args(node)
|
||||
}
|
||||
g.write(')')
|
||||
}
|
||||
g.is_c_call = false
|
||||
g.is_json_fn = false
|
||||
if free_tmp_arg_vars && g.tmp_arg_vars_to_free.len > 0 {
|
||||
if free_tmp_arg_vars { // && tmp_arg_vars_to_free.len > 0 {
|
||||
// g.writeln(';')
|
||||
// g.write(cur_line + ' /* cur line*/')
|
||||
// g.write(tmp)
|
||||
// Now free the tmp arg vars right after the function call
|
||||
g.writeln(';')
|
||||
for tmp in g.tmp_arg_vars_to_free {
|
||||
g.writeln('string_free(&$tmp);')
|
||||
for i, arg in node.args {
|
||||
if arg.is_tmp_autofree {
|
||||
fn_name := node.name.replace('.', '_')
|
||||
tmp := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||
g.writeln('string_free(&$tmp);')
|
||||
}
|
||||
}
|
||||
g.writeln('')
|
||||
g.tmp_arg_vars_to_free.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||
// fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type, tmp_arg_vars_to_free []string) {
|
||||
// fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||
fn (mut g Gen) call_args(node ast.CallExpr) {
|
||||
args := if g.is_js_call { node.args[1..] } else { node.args }
|
||||
expected_types := node.expected_arg_types
|
||||
is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].has_flag(.variadic)
|
||||
is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.has_flag(.variadic)
|
||||
gen_vargs := is_variadic && !is_forwarding_varg
|
||||
@ -635,7 +657,8 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||
break
|
||||
}
|
||||
use_tmp_var_autofree := g.autofree && g.pref.experimental && arg.typ == table.string_type &&
|
||||
g.tmp_arg_vars_to_free.len > 0
|
||||
arg.is_tmp_autofree
|
||||
// g.write('/* af=$arg.is_tmp_autofree */')
|
||||
mut is_interface := false
|
||||
// some c fn definitions dont have args (cfns.v) or are not updated in checker
|
||||
// when these are fixed we wont need this check
|
||||
@ -654,14 +677,14 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||
if is_interface {
|
||||
g.expr(arg.expr)
|
||||
} else if use_tmp_var_autofree {
|
||||
for name in g.tmp_arg_vars_to_free {
|
||||
if arg.is_tmp_autofree {
|
||||
// We saved expressions in temp variables so that they can be freed later.
|
||||
// `foo(str + str2) => x := str + str2; foo(x); x.free()`
|
||||
// g.write('_arg_expr_${g.called_fn_name}_$i')
|
||||
// Use these variables here.
|
||||
if name.contains('_$i') {
|
||||
g.write(name)
|
||||
}
|
||||
fn_name := node.name.replace('.', '_')
|
||||
name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||
g.write('/*af arg*/' + name)
|
||||
}
|
||||
} else {
|
||||
g.ref_or_deref_arg(arg, expected_types[i])
|
||||
@ -669,11 +692,9 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
|
||||
} else {
|
||||
if use_tmp_var_autofree {
|
||||
// TODO copypasta, move to an inline fn
|
||||
for name in g.tmp_arg_vars_to_free {
|
||||
if name.contains('_$i') {
|
||||
g.write(name)
|
||||
}
|
||||
}
|
||||
fn_name := node.name.replace('.', '_')
|
||||
name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||
g.write('/*af arg2*/' + name)
|
||||
} else {
|
||||
g.expr(arg.expr)
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
|
||||
}
|
||||
}
|
||||
p.check(.lpar)
|
||||
args := p.call_args()
|
||||
mut args := p.call_args()
|
||||
last_pos := p.tok.position()
|
||||
p.check(.rpar)
|
||||
// ! in mutable methods
|
||||
|
@ -15,12 +15,17 @@ fn foo() {
|
||||
// nums.free() // this should result in a double free and a CI error
|
||||
}
|
||||
|
||||
fn handle_strings(s, p string) {
|
||||
fn handle_strings(s, p string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
fn handle_int(n int) {
|
||||
}
|
||||
|
||||
fn str_tmp_expr() {
|
||||
println('a' + 'b') // tmp expression result must be freed
|
||||
handle_strings('c' + 'd', 'e' + 'f')
|
||||
handle_strings('c' + 'd', 'e' + 'f') // multiple tmp expressions must be freed
|
||||
// handle_int(handle_strings('x' + 'y', 'f')) // exprs 2 levels deep must bee freed
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
|
Loading…
Reference in New Issue
Block a user