| 1 | module checker |
| 2 | |
| 3 | import v.ast |
| 4 | |
| 5 | pub fn (mut c Checker) lambda_expr(mut node ast.LambdaExpr, exp_typ ast.Type) ast.Type { |
| 6 | if node.is_checked { |
| 7 | if c.table.cur_concrete_types.len == 0 || node.typ == exp_typ { |
| 8 | return node.typ |
| 9 | } |
| 10 | // Re-checking with different concrete types: don't recreate the AnonFn |
| 11 | // (which would mutate the shared AST and corrupt params for other |
| 12 | // instantiations). Just update the scope variable types and return. |
| 13 | // The cgen handles per-instantiation code generation via g.cur_concrete_types. |
| 14 | exp_sym := c.table.sym(exp_typ) |
| 15 | if exp_sym.info is ast.FnType { |
| 16 | for idx, mut x in node.params { |
| 17 | if idx < exp_sym.info.func.params.len { |
| 18 | eparam_type := exp_sym.info.func.params[idx].typ |
| 19 | c.lambda_expr_fix_type_of_param(mut node, mut x, eparam_type) |
| 20 | } |
| 21 | } |
| 22 | } |
| 23 | return node.typ |
| 24 | } |
| 25 | if exp_typ in [0, ast.void_type] { |
| 26 | c.fatal('lambda expressions are allowed only in places expecting function callbacks', |
| 27 | node.pos) |
| 28 | return ast.void_type |
| 29 | } |
| 30 | exp_sym := c.table.sym(exp_typ) |
| 31 | if exp_sym.kind != .function { |
| 32 | c.error('a lambda expression was used, but `${exp_sym.kind}` was expected', node.pos) |
| 33 | return ast.void_type |
| 34 | } |
| 35 | if exp_sym.info is ast.FnType { |
| 36 | if node.params.len > exp_sym.info.func.params.len { |
| 37 | c.error('lambda expression has ${node.params.len} params, but the expected fn callback needs ${exp_sym.info.func.params.len} params', |
| 38 | node.pos) |
| 39 | return ast.void_type |
| 40 | } |
| 41 | mut params := []ast.Param{} |
| 42 | mut generic_names := []string{} |
| 43 | for idx, mut x in node.params { |
| 44 | eparam := exp_sym.info.func.params[idx] |
| 45 | eparam_type := eparam.typ |
| 46 | c.lambda_expr_fix_type_of_param(mut node, mut x, eparam_type) |
| 47 | c.lambda_expr_push_generic_names(mut generic_names, eparam_type) |
| 48 | params << ast.Param{ |
| 49 | pos: x.pos |
| 50 | name: x.name |
| 51 | typ: eparam_type |
| 52 | type_pos: x.pos |
| 53 | } |
| 54 | } |
| 55 | for idx in node.params.len .. exp_sym.info.func.params.len { |
| 56 | eparam_type := exp_sym.info.func.params[idx].typ |
| 57 | c.lambda_expr_push_generic_names(mut generic_names, eparam_type) |
| 58 | } |
| 59 | c.append_omitted_callback_params(mut params, exp_sym.info.func.params, node.pos, |
| 60 | '__v_lambda_unused_param_', unsafe { nil }) |
| 61 | |
| 62 | is_variadic := false |
| 63 | return_type := exp_sym.info.func.return_type |
| 64 | return_type_pos := node.pos |
| 65 | c.lambda_expr_push_generic_names(mut generic_names, return_type) |
| 66 | |
| 67 | mut stmts := []ast.Stmt{} |
| 68 | mut has_return := false |
| 69 | if return_type.clear_option_and_result() == ast.void_type { |
| 70 | stmts << ast.ExprStmt{ |
| 71 | pos: node.pos |
| 72 | expr: node.expr |
| 73 | is_expr: false |
| 74 | typ: return_type |
| 75 | } |
| 76 | if mut node.expr is ast.CallExpr && node.expr.is_return_used { |
| 77 | node.expr.is_return_used = false |
| 78 | } |
| 79 | } else { |
| 80 | stmts << ast.Return{ |
| 81 | scope: node.scope |
| 82 | pos: node.pos |
| 83 | exprs: [node.expr] |
| 84 | } |
| 85 | has_return = true |
| 86 | } |
| 87 | |
| 88 | mut func := ast.Fn{ |
| 89 | params: params |
| 90 | is_variadic: is_variadic |
| 91 | return_type: return_type |
| 92 | is_method: false |
| 93 | } |
| 94 | name := c.table.get_anon_fn_name(c.file.unique_prefix, func, node.pos) |
| 95 | func.name = name |
| 96 | idx := c.table.find_or_register_fn_type(func, true, false) |
| 97 | typ := ast.new_type(idx) |
| 98 | node.func = &ast.AnonFn{ |
| 99 | decl: ast.FnDecl{ |
| 100 | name: name |
| 101 | short_name: '' |
| 102 | mod: c.file.mod.name |
| 103 | stmts: stmts |
| 104 | has_return: has_return |
| 105 | return_type: return_type |
| 106 | return_type_pos: return_type_pos |
| 107 | params: params |
| 108 | is_variadic: is_variadic |
| 109 | is_method: false |
| 110 | is_anon: true |
| 111 | no_body: false |
| 112 | pos: node.pos.extend(node.pos_end) |
| 113 | file: c.file.path |
| 114 | scope: node.scope.parent |
| 115 | generic_names: generic_names |
| 116 | } |
| 117 | typ: typ |
| 118 | } |
| 119 | if node.func.decl.generic_names.len > 0 { |
| 120 | c.table.register_fn_generic_types(node.func.decl.fkey()) |
| 121 | } |
| 122 | c.anon_fn(mut node.func) |
| 123 | } |
| 124 | node.is_checked = true |
| 125 | node.typ = exp_typ |
| 126 | |
| 127 | return exp_typ |
| 128 | } |
| 129 | |
| 130 | fn (mut c Checker) lambda_expr_push_generic_names(mut generic_names []string, typ ast.Type) { |
| 131 | for generic_name in c.table.generic_type_names(typ) { |
| 132 | if generic_name !in generic_names { |
| 133 | generic_names << generic_name |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | pub fn (mut c Checker) lambda_expr_fix_type_of_param(mut node ast.LambdaExpr, mut pident ast.Ident, ptype ast.Type) { |
| 139 | if mut v := node.scope.find_var(pident.name) { |
| 140 | v.is_arg = true |
| 141 | v.typ = ptype |
| 142 | v.is_auto_deref = ptype.is_ptr() |
| 143 | v.expr = ast.empty_expr |
| 144 | if ptype.has_flag(.generic) { |
| 145 | v.ct_type_var = .generic_param |
| 146 | } |
| 147 | } |
| 148 | if pident.kind != .blank_ident { |
| 149 | c.ident(mut pident) |
| 150 | pident.obj.typ = ptype |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | pub fn (mut c Checker) support_lambda_expr_in_sort(param_type ast.Type, return_type ast.Type, mut expr ast.LambdaExpr) { |
| 155 | mut expected_fn := ast.Fn{ |
| 156 | params: [ |
| 157 | ast.Param{ |
| 158 | name: 'zza' |
| 159 | typ: param_type |
| 160 | }, |
| 161 | ast.Param{ |
| 162 | name: 'zzb' |
| 163 | typ: param_type |
| 164 | }, |
| 165 | ] |
| 166 | return_type: return_type |
| 167 | } |
| 168 | expected_fn_type := ast.new_type(c.table.find_or_register_fn_type(expected_fn, true, false)) |
| 169 | c.lambda_expr(mut expr, expected_fn_type) |
| 170 | } |
| 171 | |
| 172 | pub fn (mut c Checker) support_lambda_expr_one_param(param_type ast.Type, return_type ast.Type, mut expr ast.LambdaExpr) { |
| 173 | expected_fn := ast.Fn{ |
| 174 | params: [ |
| 175 | ast.Param{ |
| 176 | name: 'xx' |
| 177 | typ: if c.table.final_sym(param_type).kind == .function { |
| 178 | ast.voidptr_type |
| 179 | } else { |
| 180 | param_type |
| 181 | } |
| 182 | }, |
| 183 | ] |
| 184 | return_type: if c.table.final_sym(return_type).kind == .function { |
| 185 | ast.voidptr_type |
| 186 | } else { |
| 187 | return_type |
| 188 | } |
| 189 | } |
| 190 | cb_type := c.table.find_or_register_fn_type(expected_fn, true, false) |
| 191 | expected_fn_type := ast.new_type(cb_type) |
| 192 | c.lambda_expr(mut expr, expected_fn_type) |
| 193 | } |
| 194 | |