vxx2 / vlib / v3 / transform / return.v
501 lines · 477 sloc · 16.61 KB · 2b81918e62db9ab38787518603a7f93bee80868c
Raw
1module transform
2
3import v3.flat
4
5// transform_return_with_sumtype_wrap checks if a return statement returns a
6// variant value where the function's return type is a sum type. In that case,
7// the variant value may need to be wrapped in the sum type's tagged union.
8//
9// For now, passes through unchanged since the C gen handles wrapping currently.
10// This is a hook for future sum type return wrapping at the transform level.
11fn (mut t Transformer) transform_return_with_sumtype_wrap(id flat.NodeId, node flat.Node) []flat.NodeId {
12 if node.children_count == 0 {
13 return arr1(id)
14 }
15 // Check if current function returns a sum type
16 if t.cur_fn_ret_type.len == 0 || t.cur_fn_ret_type !in t.sum_types {
17 return arr1(id)
18 }
19 // Check if the return value is a struct init whose type is a variant
20 child_id := t.a.child(&node, 0)
21 child := t.a.nodes[int(child_id)]
22 if child.kind == .struct_init && child.value.len > 0 {
23 variants := t.sum_types[t.cur_fn_ret_type]
24 for v in variants {
25 if v == child.value {
26 // This is a variant being returned as a sum type.
27 // For now, pass through - C gen handles the wrapping.
28 // TODO: Generate explicit sum type wrapping here.
29 return arr1(id)
30 }
31 }
32 }
33 return arr1(id)
34}
35
36// branch_tail_expr extracts the tail EXPRESSION id from a branch block,
37// unwrapping a trailing `.expr_stmt` so we get the expression inside it
38// rather than the statement wrapper.
39fn (t &Transformer) branch_tail_expr(block_id flat.NodeId) flat.NodeId {
40 if int(block_id) < 0 {
41 return block_id
42 }
43 block := t.a.nodes[int(block_id)]
44 if block.kind != .block || block.children_count == 0 {
45 return block_id
46 }
47 last_id := t.a.child(&block, block.children_count - 1)
48 last := t.a.nodes[int(last_id)]
49 if last.kind == .expr_stmt && last.children_count > 0 {
50 return t.a.child(&last, 0)
51 }
52 return last_id
53}
54
55// make_return builds a `return <val>` statement node with the given type.
56fn (mut t Transformer) make_return(val flat.NodeId, ret_typ string) flat.NodeId {
57 return t.make_return_values(arr1(val), ret_typ)
58}
59
60fn (mut t Transformer) make_return_values(vals []flat.NodeId, ret_typ string) flat.NodeId {
61 start := t.a.children.len
62 for val in vals {
63 t.a.children << val
64 }
65 return t.a.add_node(flat.Node{
66 kind: .return_stmt
67 children_start: start
68 children_count: flat.child_count(vals.len)
69 typ: ret_typ
70 })
71}
72
73// return_expr_is_err supports return expr is err handling for Transformer.
74fn (t &Transformer) return_expr_is_err(id flat.NodeId) bool {
75 if int(id) < 0 {
76 return false
77 }
78 node := t.a.nodes[int(id)]
79 return node.kind == .ident && node.value == 'err'
80}
81
82fn (mut t Transformer) try_return_direct_optional_expr(node flat.Node) ?[]flat.NodeId {
83 if node.children_count != 1 || !t.is_optional_type_name(t.cur_fn_ret_type) {
84 return none
85 }
86 child_id := t.a.child(&node, 0)
87 child := t.a.nodes[int(child_id)]
88 if child.kind == .none_expr || !t.return_expr_is_optional_result(child_id) {
89 return none
90 }
91 ret_type := t.qualify_optional_type(t.cur_fn_ret_type)
92 expr_type := t.qualify_optional_type(t.optional_result_expr_type_name(child_id))
93 if !t.optional_types_match(ret_type, expr_type) {
94 return none
95 }
96 new_expr := t.transform_expr(child_id)
97 mut result := []flat.NodeId{}
98 t.drain_pending(mut result)
99 result << t.make_return(new_expr, ret_type)
100 return result
101}
102
103fn (t &Transformer) optional_types_match(a string, b string) bool {
104 if !t.is_optional_type_name(a) || !t.is_optional_type_name(b) || a[0] != b[0] {
105 return false
106 }
107 a_base := t.normalize_type_alias(t.optional_base_type(a))
108 b_base := t.normalize_type_alias(t.optional_base_type(b))
109 if a_base == b_base {
110 return true
111 }
112 return a_base.all_after_last('.') == b_base.all_after_last('.')
113}
114
115// try_expand_return_optional_expr
116// supports helper handling in transform.
117fn (mut t Transformer) try_expand_return_optional_expr(node flat.Node) ?[]flat.NodeId {
118 if node.children_count != 1 || !t.is_optional_type_name(t.cur_fn_ret_type) {
119 return none
120 }
121 child_id := t.a.child(&node, 0)
122 child := t.a.nodes[int(child_id)]
123 if child.kind == .none_expr {
124 return none
125 }
126 if !t.return_expr_is_optional_result(child_id) {
127 return none
128 }
129 expr_type0 := t.optional_result_expr_type_name(child_id)
130 if !t.is_optional_type_name(expr_type0) {
131 return none
132 }
133 ret_type := t.qualify_optional_type(t.cur_fn_ret_type)
134 expr_type := t.qualify_optional_type(expr_type0)
135 new_expr := t.transform_expr(child_id)
136 mut result := []flat.NodeId{}
137 t.drain_pending(mut result)
138 tmp_name := t.new_temp('return_opt')
139 result << t.make_decl_assign_typed(tmp_name, new_expr, expr_type)
140 tmp_ident := t.make_ident(tmp_name)
141 ok_cond := t.make_selector(tmp_ident, 'ok', 'bool')
142 value := t.make_selector(t.make_ident(tmp_name), 'value', t.optional_base_type(expr_type))
143 then_block := t.make_block(arr1(t.make_return(value, ret_type)))
144 err_expr := t.make_selector(t.make_ident(tmp_name), 'err', 'IError')
145 else_block := t.make_block(arr1(t.make_none_return_stmt_with_err_expr(err_expr)))
146 result << t.make_if(ok_cond, then_block, else_block)
147 return result
148}
149
150// return_expr_is_optional_result supports return expr is optional result handling for Transformer.
151fn (t &Transformer) return_expr_is_optional_result(id flat.NodeId) bool {
152 if int(id) < 0 {
153 return false
154 }
155 node := t.a.nodes[int(id)]
156 if node.kind == .call && !isnil(t.tc) {
157 if name := t.tc.resolved_call_name(id) {
158 if ret := t.tc.fn_ret_types[name] {
159 return t.is_optional_type_name(ret.name())
160 }
161 }
162 return t.is_optional_type_name(t.get_call_return_type(id, node))
163 }
164 if node.kind == .ident {
165 typ := t.var_type(node.value)
166 return t.is_optional_type_name(typ) || t.is_optional_type_name(node.typ)
167 }
168 if node.kind in [.selector, .index, .if_expr, .match_stmt, .or_expr] {
169 return t.is_optional_type_name(t.node_type(id))
170 }
171 return t.is_optional_type_name(node.typ)
172}
173
174// return_block_from_branch builds a block that keeps leading statements
175// (transformed) and turns the tail expression of the branch into a `return`.
176fn (mut t Transformer) return_block_from_branch(branch_id flat.NodeId, ret_typ string, extra_return_vals []flat.NodeId) flat.NodeId {
177 branch := t.a.nodes[int(branch_id)]
178 if branch.kind == .return_stmt {
179 mut all := []flat.NodeId{}
180 ret_stmts := t.transform_stmt(branch_id)
181 t.drain_pending(mut all)
182 for stmt in ret_stmts {
183 all << stmt
184 }
185 return t.make_block(all)
186 }
187 if branch.kind != .block {
188 // single expression branch: just `return <expr>`
189 mut all := []flat.NodeId{}
190 ret_vals := t.return_values_with_extra(branch_id, extra_return_vals)
191 t.drain_pending(mut all)
192 all << t.make_return_values(ret_vals, ret_typ)
193 return t.make_block(all)
194 }
195 mut stmt_ids := []flat.NodeId{}
196 for i in 0 .. branch.children_count {
197 stmt_ids << t.a.child(&branch, i)
198 }
199 if stmt_ids.len == 0 {
200 return t.make_block([]flat.NodeId{})
201 }
202 // all but the last are kept as statements (transformed); the last becomes a return
203 lead := stmt_ids[..stmt_ids.len - 1].clone()
204 new_lead := t.transform_stmts(lead)
205 tail_id := stmt_ids[stmt_ids.len - 1]
206 tail := t.a.nodes[int(tail_id)]
207 tail_expr := t.branch_tail_expr(branch_id)
208 mut all := []flat.NodeId{}
209 for s in new_lead {
210 all << s
211 }
212 if tail.kind == .return_stmt {
213 ret_stmts := t.transform_stmt(tail_id)
214 t.drain_pending(mut all)
215 for stmt in ret_stmts {
216 all << stmt
217 }
218 return t.make_block(all)
219 }
220 ret_vals := t.return_values_with_extra(tail_expr, extra_return_vals)
221 t.drain_pending(mut all)
222 ret := t.make_return_values(ret_vals, ret_typ)
223 all << ret
224 return t.make_block(all)
225}
226
227// build_return_if_chain recursively converts an if_expr (possibly an else-if
228// chain) into an if-statement whose branch tails are `return` statements.
229fn (mut t Transformer) build_return_if_chain(if_id flat.NodeId, ret_typ string, extra_return_vals []flat.NodeId) flat.NodeId {
230 if_node := t.a.nodes[int(if_id)]
231 cond_id := t.a.child(&if_node, 0)
232 cond_smartcasts := t.extract_all_is_exprs(cond_id)
233 new_cond := t.transform_and_chain_smartcasts(cond_id)
234 then_id := t.a.child(&if_node, 1)
235 for info in cond_smartcasts {
236 t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
237 }
238 then_block := t.return_block_from_branch(then_id, ret_typ, extra_return_vals)
239 for _ in cond_smartcasts {
240 t.pop_smartcast()
241 }
242 mut else_block := flat.empty_node
243 if if_node.children_count >= 3 {
244 else_id := t.a.child(&if_node, 2)
245 else_node := t.a.nodes[int(else_id)]
246 if else_node.kind == .if_expr {
247 // else-if chain: recurse, wrap resulting if-stmt in a block
248 inner := t.build_return_if_chain(else_id, ret_typ, extra_return_vals)
249 else_block = t.make_block(arr1(inner))
250 } else {
251 else_block = t.return_block_from_branch(else_id, ret_typ, extra_return_vals)
252 }
253 }
254 return t.make_if(new_cond, then_block, else_block)
255}
256
257// try_expand_return_if detects a `return if cond { a } else { b }` pattern
258// and expands it into `if cond { return a } else { return b }`.
259// This simplification makes the C backend's job easier since it avoids
260// needing statement-expressions for the if-as-value in the return position.
261//
262// Leading statements in each branch are preserved, and a trailing `.expr_stmt`
263// is unwrapped so the inner expression (not the statement) is returned.
264//
265// Returns the expanded if-statement as a single-element array, or none if the
266// pattern does not match. A plain `return if x {..}` with no else (if_expr with
267// fewer than 3 children) is left unexpanded.
268fn (mut t Transformer) try_expand_return_if(_id flat.NodeId, node flat.Node) ?[]flat.NodeId {
269 if node.children_count == 0 {
270 return none
271 }
272 val_id := t.a.child(&node, 0)
273 if int(val_id) < 0 || int(val_id) >= t.a.nodes.len {
274 return none
275 }
276 val_node := t.a.nodes[int(val_id)]
277 if val_node.kind != .if_expr || val_node.children_count < 3 {
278 return none
279 }
280 mut extra_return_vals := []flat.NodeId{}
281 for i in 1 .. node.children_count {
282 extra_return_vals << t.a.child(&node, i)
283 }
284 return arr1(t.build_return_if_chain(val_id, node.typ, extra_return_vals))
285}
286
287// match_branch_return_block supports match branch return block handling for Transformer.
288fn (mut t Transformer) match_branch_return_block(branch flat.Node, body_start_idx int, ret_typ string) flat.NodeId {
289 mut body_ids := []flat.NodeId{}
290 for i in body_start_idx .. branch.children_count {
291 body_ids << t.a.child(&branch, i)
292 }
293 if body_ids.len == 0 {
294 return t.make_block([]flat.NodeId{})
295 }
296 mut all := []flat.NodeId{}
297 if body_ids.len > 1 {
298 lead := body_ids[..body_ids.len - 1].clone()
299 for stmt in t.transform_stmts(lead) {
300 all << stmt
301 }
302 }
303 tail_id := body_ids[body_ids.len - 1]
304 tail := t.a.nodes[int(tail_id)]
305 if tail.kind == .return_stmt {
306 for stmt in t.transform_stmt(tail_id) {
307 all << stmt
308 }
309 return t.make_block(all)
310 }
311 tail_expr := if tail.kind == .expr_stmt && tail.children_count > 0 {
312 t.a.child(&tail, 0)
313 } else {
314 tail_id
315 }
316 tail_expr_node := t.a.nodes[int(tail_expr)]
317 if tail_expr_node.kind == .match_stmt {
318 nested_return := t.make_return(tail_expr, ret_typ)
319 for stmt in t.transform_stmt(nested_return) {
320 all << stmt
321 }
322 return t.make_block(all)
323 }
324 expected_ret := t.optional_base_type(ret_typ)
325 actual_tail := if tail_expr_node.kind == .enum_val {
326 t.transform_enum_shorthand(tail_expr, tail_expr_node, expected_ret)
327 } else {
328 tail_expr
329 }
330 // Convert a fixed-array branch value (e.g. a fixed-array const) to a dynamic
331 // array when the function returns `[]T`, matching a plain `return <expr>`.
332 ret_val := if converted := t.fixed_array_return_value(actual_tail) {
333 converted
334 } else {
335 t.wrap_sum_return_expr(actual_tail)
336 }
337 t.drain_pending(mut all)
338 all << t.make_return(ret_val, ret_typ)
339 return t.make_block(all)
340}
341
342// build_return_match_chain builds return match chain data for transform.
343fn (mut t Transformer) build_return_match_chain(match_expr_id flat.NodeId, orig_expr_id flat.NodeId, branches []flat.NodeId, idx int, ret_typ string) flat.NodeId {
344 if idx >= branches.len {
345 return t.a.add(flat.NodeKind.empty)
346 }
347 branch := t.a.nodes[int(branches[idx])]
348 is_else := branch.value == 'else'
349 body_start_idx := if is_else { 0 } else { t.count_conds(branch) }
350 if !is_else && t.match_branch_all_type_patterns(branch) && t.count_conds(branch) > 1 {
351 return t.build_return_match_type_branch_chain(match_expr_id, orig_expr_id, branch,
352 branches, idx, 0, ret_typ)
353 }
354
355 mut sc_pushed := 0
356 if !is_else {
357 n_conds := t.count_conds(branch)
358 if n_conds == 1 {
359 cond_val_id := t.a.child(&branch, 0)
360 if variant_name := t.match_type_pattern(cond_val_id) {
361 subj := t.expr_key(match_expr_id)
362 sum_name := t.find_sum_type_for_variant(variant_name)
363 if subj.len > 0 && sum_name.len > 0 {
364 t.push_smartcast(subj, variant_name, sum_name)
365 sc_pushed++
366 }
367 orig_subj := t.expr_key(orig_expr_id)
368 if orig_subj.len > 0 && orig_subj != subj && sum_name.len > 0 {
369 t.push_smartcast(orig_subj, variant_name, sum_name)
370 sc_pushed++
371 }
372 }
373 }
374 }
375
376 body_block := t.match_branch_return_block(branch, body_start_idx, ret_typ)
377 for _ in 0 .. sc_pushed {
378 t.pop_smartcast()
379 }
380 if is_else {
381 return body_block
382 }
383
384 cond_id := t.build_match_cond(match_expr_id, branch)
385 mut if_ids := []flat.NodeId{}
386 if_ids << cond_id
387 if_ids << body_block
388 if idx + 1 < branches.len {
389 if_ids << t.build_return_match_chain(match_expr_id, orig_expr_id, branches, idx + 1,
390 ret_typ)
391 }
392 if_start := t.a.children.len
393 for id in if_ids {
394 t.a.children << id
395 }
396 return t.a.add_node(flat.Node{
397 kind: .if_expr
398 children_start: if_start
399 children_count: flat.child_count(if_ids.len)
400 })
401}
402
403// build_return_match_type_branch_chain supports build_return_match_type_branch_chain handling.
404fn (mut t Transformer) build_return_match_type_branch_chain(match_expr_id flat.NodeId, orig_expr_id flat.NodeId, branch flat.Node, branches []flat.NodeId, idx int, cond_idx int, ret_typ string) flat.NodeId {
405 n_conds := t.count_conds(branch)
406 if cond_idx >= n_conds {
407 return if idx + 1 < branches.len {
408 t.build_return_match_chain(match_expr_id, orig_expr_id, branches, idx + 1, ret_typ)
409 } else {
410 t.a.add(flat.NodeKind.empty)
411 }
412 }
413 cond_val_id := t.a.child(&branch, cond_idx)
414 variant_name := t.match_type_pattern(cond_val_id) or {
415 return t.build_return_match_chain(match_expr_id, orig_expr_id, branches, idx + 1, ret_typ)
416 }
417 is_start := t.a.children.len
418 t.a.children << match_expr_id
419 is_id := t.a.add_node(flat.Node{
420 kind: .is_expr
421 value: variant_name
422 children_start: is_start
423 children_count: 1
424 })
425 cond_id := t.transform_is_expr(is_id, t.a.nodes[int(is_id)])
426
427 mut sc_pushed := 0
428 subj := t.expr_key(match_expr_id)
429 sum_name := t.find_sum_type_for_variant(variant_name)
430 if subj.len > 0 && sum_name.len > 0 {
431 t.push_smartcast(subj, variant_name, sum_name)
432 sc_pushed++
433 }
434 orig_subj := t.expr_key(orig_expr_id)
435 if orig_subj.len > 0 && orig_subj != subj && sum_name.len > 0 {
436 t.push_smartcast(orig_subj, variant_name, sum_name)
437 sc_pushed++
438 }
439 body_block := t.match_branch_return_block(branch, n_conds, ret_typ)
440 for _ in 0 .. sc_pushed {
441 t.pop_smartcast()
442 }
443
444 else_part := t.build_return_match_type_branch_chain(match_expr_id, orig_expr_id, branch,
445 branches, idx, cond_idx + 1, ret_typ)
446 start := t.a.children.len
447 t.a.children << cond_id
448 t.a.children << body_block
449 t.a.children << else_part
450 return t.a.add_node(flat.Node{
451 kind: .if_expr
452 children_start: start
453 children_count: 3
454 })
455}
456
457// try_expand_return_match detects a `return match x { ... }` pattern and expands
458// it into an if/else-if chain where every branch tail is an explicit return.
459fn (mut t Transformer) try_expand_return_match(_id flat.NodeId, node flat.Node) ?[]flat.NodeId {
460 if node.children_count == 0 {
461 return none
462 }
463 val_id := t.a.child(&node, 0)
464 if int(val_id) < 0 || int(val_id) >= t.a.nodes.len {
465 return none
466 }
467 val := t.a.nodes[int(val_id)]
468 if val.kind != .match_stmt || val.children_count < 2 {
469 return none
470 }
471 match_expr_id := t.a.child(&val, 0)
472 if int(match_expr_id) < 0 || int(match_expr_id) >= t.a.nodes.len {
473 return none
474 }
475 match_expr := t.a.nodes[int(match_expr_id)]
476 needs_temp := match_expr.kind !in [.ident, .int_literal, .bool_literal, .string_literal,
477 .char_literal]
478
479 mut result := []flat.NodeId{}
480 mut actual_expr_id := flat.empty_node
481 if needs_temp {
482 tmp_name := t.new_temp('match')
483 match_type := t.node_type(match_expr_id)
484 new_expr := t.transform_expr(match_expr_id)
485 t.drain_pending(mut result)
486 result << t.make_decl_assign_typed(tmp_name, new_expr, match_type)
487 actual_expr_id = t.make_ident(tmp_name)
488 t.a.nodes[int(actual_expr_id)].typ = match_type
489 t.set_var_type(tmp_name, match_type)
490 } else {
491 actual_expr_id = t.transform_expr(match_expr_id)
492 t.drain_pending(mut result)
493 }
494
495 mut branches := []flat.NodeId{}
496 for i in 1 .. val.children_count {
497 branches << t.a.child(&val, i)
498 }
499 result << t.build_return_match_chain(actual_expr_id, match_expr_id, branches, 0, node.typ)
500 return result
501}
502