mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
all: add $res compile time function to get returned value in defer block (#18382)
This commit is contained in:
parent
ac32d2a803
commit
dbd251793e
36
doc/docs.md
36
doc/docs.md
@ -2119,6 +2119,42 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To access the result of the function inside a `defer` block the `$res()` expression can be used.
|
||||||
|
`$res()` is only used when a single value is returned, while on multi-return the `$res(idx)`
|
||||||
|
is parameterized.
|
||||||
|
|
||||||
|
```v ignore
|
||||||
|
fn (mut app App) auth_middleware() bool {
|
||||||
|
defer {
|
||||||
|
if !$res() {
|
||||||
|
app.response.status_code = 401
|
||||||
|
app.response.body = 'Unauthorized'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header := app.get_header('Authorization')
|
||||||
|
if header == '' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut app App) auth_with_user_middleware() (bool, string) {
|
||||||
|
defer {
|
||||||
|
if !$res(0) {
|
||||||
|
app.response.status_code = 401
|
||||||
|
app.response.body = 'Unauthorized'
|
||||||
|
} else {
|
||||||
|
app.user = $res(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header := app.get_header('Authorization')
|
||||||
|
if header == '' {
|
||||||
|
return false, ''
|
||||||
|
}
|
||||||
|
return true, 'TestUser'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Goto
|
### Goto
|
||||||
|
|
||||||
V allows unconditionally jumping to a label with `goto`. The label name must be contained
|
V allows unconditionally jumping to a label with `goto`. The label name must be contained
|
||||||
|
@ -115,6 +115,7 @@ mut:
|
|||||||
comptime_call_pos int // needed for correctly checking use before decl for templates
|
comptime_call_pos int // needed for correctly checking use before decl for templates
|
||||||
goto_labels map[string]ast.GotoLabel // to check for unused goto labels
|
goto_labels map[string]ast.GotoLabel // to check for unused goto labels
|
||||||
enum_data_type ast.Type
|
enum_data_type ast.Type
|
||||||
|
fn_return_type ast.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker {
|
pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker {
|
||||||
|
@ -153,6 +153,40 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
|
|||||||
// assume string for now
|
// assume string for now
|
||||||
return ast.string_type
|
return ast.string_type
|
||||||
}
|
}
|
||||||
|
if node.method_name == 'res' {
|
||||||
|
if !c.inside_defer {
|
||||||
|
c.error('`res` can only be used in defer blocks', node.pos)
|
||||||
|
return ast.void_type
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.fn_return_type == ast.void_type {
|
||||||
|
c.error('`res` can only be used in functions that returns something', node.pos)
|
||||||
|
return ast.void_type
|
||||||
|
}
|
||||||
|
|
||||||
|
sym := c.table.sym(c.fn_return_type)
|
||||||
|
|
||||||
|
if c.fn_return_type.has_flag(.result) {
|
||||||
|
c.error('`res` cannot be used in functions that returns a Result', node.pos)
|
||||||
|
return ast.void_type
|
||||||
|
}
|
||||||
|
|
||||||
|
if sym.info is ast.MultiReturn {
|
||||||
|
if node.args_var == '' {
|
||||||
|
c.error('`res` requires an index of the returned value', node.pos)
|
||||||
|
return ast.void_type
|
||||||
|
}
|
||||||
|
idx := node.args_var.int()
|
||||||
|
if idx < 0 || idx >= sym.info.types.len {
|
||||||
|
c.error('index ${idx} out of range of ${sym.info.types.len} return types',
|
||||||
|
node.pos)
|
||||||
|
return ast.void_type
|
||||||
|
}
|
||||||
|
return sym.info.types[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.fn_return_type
|
||||||
|
}
|
||||||
if node.is_vweb {
|
if node.is_vweb {
|
||||||
return ast.string_type
|
return ast.string_type
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.fn_return_type = node.return_type
|
||||||
if node.return_type != ast.void_type {
|
if node.return_type != ast.void_type {
|
||||||
if ct_attr_idx := node.attrs.find_comptime_define() {
|
if ct_attr_idx := node.attrs.find_comptime_define() {
|
||||||
sexpr := node.attrs[ct_attr_idx].ct_expr.str()
|
sexpr := node.attrs[ct_attr_idx].ct_expr.str()
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv:3:11: error: index 2 out of range of 2 return types
|
||||||
|
1 | fn test() (string, string) {
|
||||||
|
2 | defer {
|
||||||
|
3 | println($res(2))
|
||||||
|
| ~~~~~~~
|
||||||
|
4 | }
|
||||||
|
5 | return 'test', 'test2'
|
@ -0,0 +1,6 @@
|
|||||||
|
fn test() (string, string) {
|
||||||
|
defer {
|
||||||
|
println($res(2))
|
||||||
|
}
|
||||||
|
return 'test', 'test2'
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv:3:11: error: `res` requires an index of the returned value
|
||||||
|
1 | fn test() (string, string) {
|
||||||
|
2 | defer {
|
||||||
|
3 | println($res())
|
||||||
|
| ~~~~~~
|
||||||
|
4 | }
|
||||||
|
5 | return 'test', 'test2'
|
@ -0,0 +1,6 @@
|
|||||||
|
fn test() (string, string) {
|
||||||
|
defer {
|
||||||
|
println($res())
|
||||||
|
}
|
||||||
|
return 'test', 'test2'
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv:3:11: error: `res` can only be used in functions that returns something
|
||||||
|
1 | fn test() {
|
||||||
|
2 | defer {
|
||||||
|
3 | println($res())
|
||||||
|
| ~~~~~~
|
||||||
|
4 | }
|
||||||
|
5 | }
|
@ -0,0 +1,5 @@
|
|||||||
|
fn test() {
|
||||||
|
defer {
|
||||||
|
println($res())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv:3:11: error: `res` cannot be used in functions that returns a Result
|
||||||
|
1 | fn test() !string {
|
||||||
|
2 | defer {
|
||||||
|
3 | println($res())
|
||||||
|
| ~~~~~~
|
||||||
|
4 | }
|
||||||
|
5 | return 'test'
|
@ -0,0 +1,6 @@
|
|||||||
|
fn test() !string {
|
||||||
|
defer {
|
||||||
|
println($res())
|
||||||
|
}
|
||||||
|
return 'test'
|
||||||
|
}
|
6
vlib/v/checker/tests/res_use_outside_defer.out
Normal file
6
vlib/v/checker/tests/res_use_outside_defer.out
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
vlib/v/checker/tests/res_use_outside_defer.vv:2:10: error: `res` can only be used in defer blocks
|
||||||
|
1 | fn test() string {
|
||||||
|
2 | println($res())
|
||||||
|
| ~~~~~~
|
||||||
|
3 | return 'test'
|
||||||
|
4 | }
|
4
vlib/v/checker/tests/res_use_outside_defer.vv
Normal file
4
vlib/v/checker/tests/res_use_outside_defer.vv
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
fn test() string {
|
||||||
|
println($res())
|
||||||
|
return 'test'
|
||||||
|
}
|
@ -1969,6 +1969,13 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) {
|
|||||||
node.method_name in ['compile_error', 'compile_warn'] {
|
node.method_name in ['compile_error', 'compile_warn'] {
|
||||||
f.write("\$${node.method_name}('${node.args_var}')")
|
f.write("\$${node.method_name}('${node.args_var}')")
|
||||||
}
|
}
|
||||||
|
node.method_name == 'res' {
|
||||||
|
if node.args_var != '' {
|
||||||
|
f.write('\$res(${node.args_var})')
|
||||||
|
} else {
|
||||||
|
f.write('\$res()')
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
inner_args := if node.args_var != '' {
|
inner_args := if node.args_var != '' {
|
||||||
node.args_var
|
node.args_var
|
||||||
|
@ -247,6 +247,7 @@ mut:
|
|||||||
|
|
||||||
has_reflection bool
|
has_reflection bool
|
||||||
reflection_strings &map[string]int
|
reflection_strings &map[string]int
|
||||||
|
defer_return_tmp_var string
|
||||||
}
|
}
|
||||||
|
|
||||||
// global or const variable definition string
|
// global or const variable definition string
|
||||||
@ -4699,6 +4700,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
tmpvar := g.new_tmp_var()
|
tmpvar := g.new_tmp_var()
|
||||||
|
g.defer_return_tmp_var = tmpvar
|
||||||
mut ret_typ := g.typ(g.unwrap_generic(fn_ret_type))
|
mut ret_typ := g.typ(g.unwrap_generic(fn_ret_type))
|
||||||
if fn_ret_type.has_flag(.generic) && fn_return_is_fixed_array {
|
if fn_ret_type.has_flag(.generic) && fn_return_is_fixed_array {
|
||||||
ret_typ = '_v_${ret_typ}'
|
ret_typ = '_v_${ret_typ}'
|
||||||
@ -4742,7 +4744,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.write_defer_stmts_when_needed()
|
g.write_defer_stmts_when_needed()
|
||||||
g.writeln('return ${tmpvar};')
|
g.writeln('return ${tmpvar}; //test')
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -4770,7 +4772,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||||||
g.writeln(';')
|
g.writeln(';')
|
||||||
if use_tmp_var {
|
if use_tmp_var {
|
||||||
g.write_defer_stmts_when_needed()
|
g.write_defer_stmts_when_needed()
|
||||||
g.writeln('return ${tmpvar};')
|
g.writeln('return ${tmpvar}; //test1')
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -4878,7 +4880,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||||||
g.writeln(';')
|
g.writeln(';')
|
||||||
}
|
}
|
||||||
g.write_defer_stmts_when_needed()
|
g.write_defer_stmts_when_needed()
|
||||||
g.writeln('return ${tmpvar};')
|
g.writeln('return ${tmpvar}; //test2')
|
||||||
has_semicolon = true
|
has_semicolon = true
|
||||||
}
|
}
|
||||||
} else if node.exprs.len >= 1 {
|
} else if node.exprs.len >= 1 {
|
||||||
@ -4917,7 +4919,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||||||
g.writeln(' }, (${c.option_name}*)(&${tmpvar}), sizeof(${styp}));')
|
g.writeln(' }, (${c.option_name}*)(&${tmpvar}), sizeof(${styp}));')
|
||||||
g.write_defer_stmts_when_needed()
|
g.write_defer_stmts_when_needed()
|
||||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||||
g.writeln('return ${tmpvar};')
|
g.writeln('return ${tmpvar}; //test4')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
expr_type_is_result := match expr0 {
|
expr_type_is_result := match expr0 {
|
||||||
@ -4950,7 +4952,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||||||
g.writeln(' }, (${c.result_name}*)(&${tmpvar}), sizeof(${styp}));')
|
g.writeln(' }, (${c.result_name}*)(&${tmpvar}), sizeof(${styp}));')
|
||||||
g.write_defer_stmts_when_needed()
|
g.write_defer_stmts_when_needed()
|
||||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||||
g.writeln('return ${tmpvar};')
|
g.writeln('return ${tmpvar}; //test 4')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// autofree before `return`
|
// autofree before `return`
|
||||||
@ -5028,7 +5030,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||||||
if !g.is_builtin_mod {
|
if !g.is_builtin_mod {
|
||||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||||
}
|
}
|
||||||
g.write('return ${tmpvar}')
|
g.write('return ${tmpvar} /* test5 */')
|
||||||
has_semicolon = false
|
has_semicolon = false
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,6 +80,15 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
|
|||||||
g.write('_SLIT("${val}")')
|
g.write('_SLIT("${val}")')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if node.method_name == 'res' {
|
||||||
|
if node.args_var != '' {
|
||||||
|
g.write('${g.defer_return_tmp_var}.arg${node.args_var}')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.write('${g.defer_return_tmp_var}')
|
||||||
|
return
|
||||||
|
}
|
||||||
if node.is_vweb {
|
if node.is_vweb {
|
||||||
is_html := node.method_name == 'html'
|
is_html := node.method_name == 'html'
|
||||||
mut cur_line := ''
|
mut cur_line := ''
|
||||||
|
@ -10,7 +10,7 @@ import v.token
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig', 'compile_error',
|
supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig', 'compile_error',
|
||||||
'compile_warn']
|
'compile_warn', 'res']
|
||||||
comptime_types = ['map', 'array', 'int', 'float', 'struct', 'interface', 'enum',
|
comptime_types = ['map', 'array', 'int', 'float', 'struct', 'interface', 'enum',
|
||||||
'sumtype', 'alias', 'function', 'option']
|
'sumtype', 'alias', 'function', 'option']
|
||||||
)
|
)
|
||||||
@ -97,7 +97,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
|||||||
}
|
}
|
||||||
start_pos := p.tok.pos()
|
start_pos := p.tok.pos()
|
||||||
p.check(.dollar)
|
p.check(.dollar)
|
||||||
error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()`, `\$vweb.html()`, `\$compile_error()` and `\$compile_warn()` comptime functions are supported right now'
|
error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()`, `\$vweb.html()`, `\$compile_error()`, `\$compile_warn()` and `\$res()` comptime functions are supported right now'
|
||||||
if p.peek_tok.kind == .dot {
|
if p.peek_tok.kind == .dot {
|
||||||
name := p.check_name() // skip `vweb.html()` TODO
|
name := p.check_name() // skip `vweb.html()` TODO
|
||||||
if name != 'vweb' {
|
if name != 'vweb' {
|
||||||
@ -129,6 +129,28 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
|||||||
env_pos: start_pos
|
env_pos: start_pos
|
||||||
pos: start_pos.extend(p.prev_tok.pos())
|
pos: start_pos.extend(p.prev_tok.pos())
|
||||||
}
|
}
|
||||||
|
} else if method_name == 'res' {
|
||||||
|
mut has_args := false
|
||||||
|
mut type_index := ''
|
||||||
|
if p.tok.kind == .number {
|
||||||
|
has_args = true
|
||||||
|
type_index = p.tok.lit
|
||||||
|
p.check(.number)
|
||||||
|
}
|
||||||
|
p.check(.rpar)
|
||||||
|
if has_args {
|
||||||
|
return ast.ComptimeCall{
|
||||||
|
scope: 0
|
||||||
|
method_name: method_name
|
||||||
|
args_var: type_index
|
||||||
|
pos: start_pos.extend(p.prev_tok.pos())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.ComptimeCall{
|
||||||
|
scope: 0
|
||||||
|
method_name: method_name
|
||||||
|
pos: start_pos.extend(p.prev_tok.pos())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mut literal_string_param := if is_html { '' } else { p.tok.lit }
|
mut literal_string_param := if is_html { '' } else { p.tok.lit }
|
||||||
if p.tok.kind == .name {
|
if p.tok.kind == .name {
|
||||||
|
40
vlib/v/tests/defer_use_returned_value_test.v
Normal file
40
vlib/v/tests/defer_use_returned_value_test.v
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
struct Test {
|
||||||
|
mut:
|
||||||
|
a int
|
||||||
|
b string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut test Test) with_single_return() int {
|
||||||
|
defer {
|
||||||
|
test.a = $res()
|
||||||
|
}
|
||||||
|
return 41
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut test Test) with_multi_return() (int, string) {
|
||||||
|
defer {
|
||||||
|
test.a = $res(0)
|
||||||
|
test.b = $res(1)
|
||||||
|
}
|
||||||
|
return 41, 'foo'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_with_single_return() {
|
||||||
|
mut test := Test{
|
||||||
|
a: 0
|
||||||
|
}
|
||||||
|
assert test.with_single_return() == 41
|
||||||
|
assert test.a == 41
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_with_multi_return() {
|
||||||
|
mut test := Test{
|
||||||
|
a: 0
|
||||||
|
b: ''
|
||||||
|
}
|
||||||
|
a, b := test.with_multi_return()
|
||||||
|
assert a == 41
|
||||||
|
assert b == 'foo'
|
||||||
|
assert test.a == a
|
||||||
|
assert test.b == b
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user