v2 / vlib / v / checker / lambda_expr.v
193 lines · 183 sloc · 5.49 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1module checker
2
3import v.ast
4
5pub 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
130fn (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
138pub 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
154pub 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
172pub 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