From 82571efa68f55735afba033cc3130a69a9abcf44 Mon Sep 17 00:00:00 2001 From: "Schmieder, Louis David" Date: Thu, 8 Jun 2023 22:47:53 +0200 Subject: [PATCH 1/4] add `$res()` function to get returned value in defer block --- vlib/v/checker/checker.v | 1 + vlib/v/checker/comptime.v | 34 ++++++++++++++++ vlib/v/checker/fn.v | 1 + ..._return_value_with_index_out_of_bounds.out | 7 ++++ ...i_return_value_with_index_out_of_bounds.vv | 6 +++ ...r_use_multi_return_value_without_index.out | 7 ++++ ...er_use_multi_return_value_without_index.vv | 6 +++ ...eturned_value_when_nothing_is_returned.out | 7 ++++ ...returned_value_when_nothing_is_returned.vv | 5 +++ ...returned_value_when_result_is_returned.out | 7 ++++ ..._returned_value_when_result_is_returned.vv | 6 +++ .../v/checker/tests/res_use_outside_defer.out | 6 +++ vlib/v/checker/tests/res_use_outside_defer.vv | 4 ++ vlib/v/fmt/fmt.v | 6 +++ vlib/v/gen/c/cgen.v | 18 +++++---- vlib/v/gen/c/comptime.v | 9 +++++ vlib/v/parser/comptime.v | 26 +++++++++++- vlib/v/tests/defer_use_returned_value_test.v | 40 +++++++++++++++++++ 18 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.out create mode 100644 vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv create mode 100644 vlib/v/checker/tests/defer_use_multi_return_value_without_index.out create mode 100644 vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv create mode 100644 vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.out create mode 100644 vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv create mode 100644 vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.out create mode 100644 vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv create mode 100644 vlib/v/checker/tests/res_use_outside_defer.out create mode 100644 vlib/v/checker/tests/res_use_outside_defer.vv create mode 100644 vlib/v/tests/defer_use_returned_value_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 20d50203426574..9fafdb66b98f33 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -115,6 +115,7 @@ mut: 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 enum_data_type ast.Type + fn_return_type ast.Type } pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker { diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 53938a6411212b..08cb30142d4471 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -144,6 +144,40 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { // assume string for now 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 { return ast.string_type } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index cdd0326a8862b3..0cc480ff6b4540 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -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 ct_attr_idx := node.attrs.find_comptime_define() { sexpr := node.attrs[ct_attr_idx].ct_expr.str() diff --git a/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.out b/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.out new file mode 100644 index 00000000000000..08cdc16849ac93 --- /dev/null +++ b/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.out @@ -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' diff --git a/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv b/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv new file mode 100644 index 00000000000000..16eff3c510e42b --- /dev/null +++ b/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv @@ -0,0 +1,6 @@ +fn test() (string, string) { + defer { + println($res('2')) + } + return 'test', 'test2' +} diff --git a/vlib/v/checker/tests/defer_use_multi_return_value_without_index.out b/vlib/v/checker/tests/defer_use_multi_return_value_without_index.out new file mode 100644 index 00000000000000..70898e96c45513 --- /dev/null +++ b/vlib/v/checker/tests/defer_use_multi_return_value_without_index.out @@ -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' diff --git a/vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv b/vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv new file mode 100644 index 00000000000000..b90b2e75e2eca2 --- /dev/null +++ b/vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv @@ -0,0 +1,6 @@ +fn test() (string, string) { + defer { + println($res('')) + } + return 'test', 'test2' +} diff --git a/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.out b/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.out new file mode 100644 index 00000000000000..88094686f58e1a --- /dev/null +++ b/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.out @@ -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 | } diff --git a/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv b/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv new file mode 100644 index 00000000000000..39e1da89d88d90 --- /dev/null +++ b/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv @@ -0,0 +1,5 @@ +fn test() { + defer { + println($res('')) + } +} diff --git a/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.out b/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.out new file mode 100644 index 00000000000000..ca8995cdc5461a --- /dev/null +++ b/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.out @@ -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' diff --git a/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv b/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv new file mode 100644 index 00000000000000..2ca95106ad4d60 --- /dev/null +++ b/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv @@ -0,0 +1,6 @@ +fn test() !string { + defer { + println($res('')) + } + return 'test' +} diff --git a/vlib/v/checker/tests/res_use_outside_defer.out b/vlib/v/checker/tests/res_use_outside_defer.out new file mode 100644 index 00000000000000..54122a0db095e3 --- /dev/null +++ b/vlib/v/checker/tests/res_use_outside_defer.out @@ -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 | } diff --git a/vlib/v/checker/tests/res_use_outside_defer.vv b/vlib/v/checker/tests/res_use_outside_defer.vv new file mode 100644 index 00000000000000..af058cfbcb9b53 --- /dev/null +++ b/vlib/v/checker/tests/res_use_outside_defer.vv @@ -0,0 +1,4 @@ +fn test() string { + println($res('')) + return 'test' +} diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index bd869e163a279c..6a9b299c814cab 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1963,6 +1963,12 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) { f.write("\$pkgconfig('${node.args_var}')") } else if node.method_name in ['compile_error', 'compile_warn'] { f.write("\$${node.method_name}('${node.args_var}')") + } else if node.method_name == 'res' { + if node.args_var != '' { + f.write("\$res('${node.args_var}')") + } else { + f.write('\$res()') + } } else { inner_args := if node.args_var != '' { node.args_var diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index ec28db81a3bca6..0e31b45ea43209 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -244,8 +244,9 @@ mut: out_fn_start_pos []int // for generating multiple .c files, stores locations of all fn positions in `out` string builder static_modifier string // for parallel_cc - has_reflection bool - reflection_strings &map[string]int + has_reflection bool + reflection_strings &map[string]int + defer_return_tmp_var string } // global or const variable definition string @@ -4683,6 +4684,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { return } tmpvar := g.new_tmp_var() + g.defer_return_tmp_var = tmpvar mut ret_typ := g.typ(g.unwrap_generic(fn_ret_type)) if fn_ret_type.has_flag(.generic) && fn_return_is_fixed_array { ret_typ = '_v_${ret_typ}' @@ -4726,7 +4728,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { } } g.write_defer_stmts_when_needed() - g.writeln('return ${tmpvar};') + g.writeln('return ${tmpvar}; //test') } return } @@ -4754,7 +4756,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { g.writeln(';') if use_tmp_var { g.write_defer_stmts_when_needed() - g.writeln('return ${tmpvar};') + g.writeln('return ${tmpvar}; //test1') } return } @@ -4862,7 +4864,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { g.writeln(';') } g.write_defer_stmts_when_needed() - g.writeln('return ${tmpvar};') + g.writeln('return ${tmpvar}; //test2') has_semicolon = true } } else if node.exprs.len >= 1 { @@ -4901,7 +4903,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { g.writeln(' }, (${c.option_name}*)(&${tmpvar}), sizeof(${styp}));') g.write_defer_stmts_when_needed() g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true) - g.writeln('return ${tmpvar};') + g.writeln('return ${tmpvar}; //test4') return } expr_type_is_result := match expr0 { @@ -4934,7 +4936,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { g.writeln(' }, (${c.result_name}*)(&${tmpvar}), sizeof(${styp}));') g.write_defer_stmts_when_needed() g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true) - g.writeln('return ${tmpvar};') + g.writeln('return ${tmpvar}; //test 4') return } // autofree before `return` @@ -5008,7 +5010,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { if !g.is_builtin_mod { 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 } } else { diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 0a96569c770fb5..a226d4c4ef5d84 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -80,6 +80,15 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) { g.write('_SLIT("${val}")') 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 { is_html := node.method_name == 'html' mut cur_line := '' diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 76bc4fa2d72f4e..861a429f9493ea 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -10,7 +10,7 @@ import v.token const ( 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', 'sumtype', 'alias', 'function', 'option'] ) @@ -97,7 +97,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { } start_pos := p.tok.pos() 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 { name := p.check_name() // skip `vweb.html()` TODO if name != 'vweb' { @@ -129,6 +129,28 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { env_pos: start_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 } if p.tok.kind == .name { diff --git a/vlib/v/tests/defer_use_returned_value_test.v b/vlib/v/tests/defer_use_returned_value_test.v new file mode 100644 index 00000000000000..0006839e6cc2ba --- /dev/null +++ b/vlib/v/tests/defer_use_returned_value_test.v @@ -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 +} From 39bbc247fb6be3a64df2df7bdc2030acf73d0d64 Mon Sep 17 00:00:00 2001 From: "Schmieder, Louis David" Date: Thu, 8 Jun 2023 23:00:46 +0200 Subject: [PATCH 2/4] fix fmt --- ...defer_use_multi_return_value_with_index_out_of_bounds.vv | 2 +- .../tests/defer_use_multi_return_value_without_index.vv | 2 +- .../defer_use_returned_value_when_nothing_is_returned.vv | 2 +- .../defer_use_returned_value_when_result_is_returned.vv | 2 +- vlib/v/checker/tests/res_use_outside_defer.vv | 2 +- vlib/v/fmt/fmt.v | 2 +- vlib/v/tests/defer_use_returned_value_test.v | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv b/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv index 16eff3c510e42b..de393db906d9fe 100644 --- a/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv +++ b/vlib/v/checker/tests/defer_use_multi_return_value_with_index_out_of_bounds.vv @@ -1,6 +1,6 @@ fn test() (string, string) { defer { - println($res('2')) + println($res(2)) } return 'test', 'test2' } diff --git a/vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv b/vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv index b90b2e75e2eca2..4cd11a5cd0ef83 100644 --- a/vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv +++ b/vlib/v/checker/tests/defer_use_multi_return_value_without_index.vv @@ -1,6 +1,6 @@ fn test() (string, string) { defer { - println($res('')) + println($res()) } return 'test', 'test2' } diff --git a/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv b/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv index 39e1da89d88d90..b57a83556aad8e 100644 --- a/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv +++ b/vlib/v/checker/tests/defer_use_returned_value_when_nothing_is_returned.vv @@ -1,5 +1,5 @@ fn test() { defer { - println($res('')) + println($res()) } } diff --git a/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv b/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv index 2ca95106ad4d60..606e412a90e757 100644 --- a/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv +++ b/vlib/v/checker/tests/defer_use_returned_value_when_result_is_returned.vv @@ -1,6 +1,6 @@ fn test() !string { defer { - println($res('')) + println($res()) } return 'test' } diff --git a/vlib/v/checker/tests/res_use_outside_defer.vv b/vlib/v/checker/tests/res_use_outside_defer.vv index af058cfbcb9b53..1adca1923f52fe 100644 --- a/vlib/v/checker/tests/res_use_outside_defer.vv +++ b/vlib/v/checker/tests/res_use_outside_defer.vv @@ -1,4 +1,4 @@ fn test() string { - println($res('')) + println($res()) return 'test' } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 6a9b299c814cab..3ab5a2764a1819 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1965,7 +1965,7 @@ pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) { f.write("\$${node.method_name}('${node.args_var}')") } else if node.method_name == 'res' { if node.args_var != '' { - f.write("\$res('${node.args_var}')") + f.write('\$res(${node.args_var})') } else { f.write('\$res()') } diff --git a/vlib/v/tests/defer_use_returned_value_test.v b/vlib/v/tests/defer_use_returned_value_test.v index 0006839e6cc2ba..a8ea9df4e878fe 100644 --- a/vlib/v/tests/defer_use_returned_value_test.v +++ b/vlib/v/tests/defer_use_returned_value_test.v @@ -6,15 +6,15 @@ mut: fn (mut test Test) with_single_return() int { defer { - test.a = $res('') + test.a = $res() } return 41 } fn (mut test Test) with_multi_return() (int, string) { defer { - test.a = $res('0') - test.b = $res('1') + test.a = $res(0) + test.b = $res(1) } return 41, 'foo' } From e1af1e6ee362be923e6e731afede5c4c11421198 Mon Sep 17 00:00:00 2001 From: "Schmieder, Louis David" Date: Wed, 14 Jun 2023 15:11:46 +0200 Subject: [PATCH 3/4] add documentation --- doc/docs.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/doc/docs.md b/doc/docs.md index e13a6583bf24e2..89962d7979e58f 100644 --- a/doc/docs.md +++ b/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 +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 V allows unconditionally jumping to a label with `goto`. The label name must be contained From 6973e3305885efc893905b23abe90a8cb383bb21 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 16 Jun 2023 12:12:58 +0300 Subject: [PATCH 4/4] Update doc/docs.md --- doc/docs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs.md b/doc/docs.md index 89962d7979e58f..6a23082fe19261 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -2123,7 +2123,7 @@ To access the result of the function inside a `defer` block the `$res()` express `$res()` is only used when a single value is returned, while on multi-return the `$res(idx)` is parameterized. -```v +```v ignore fn (mut app App) auth_middleware() bool { defer { if !$res() {