v4 / vlib / v3 / transform / return.v
501 lines · 478 sloc · 16.4 KB · 351709f1031b64c6175612e9e459d8216eeca7f5
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 start := t.a.children.len
58 t.a.children << val
59 return t.a.add_node(flat.Node{
60 kind: .return_stmt
61 children_start: start
62 children_count: 1
63 typ: ret_typ
64 })
65}
66
67// return_expr_is_err supports return expr is err handling for Transformer.
68fn (t &Transformer) return_expr_is_err(id flat.NodeId) bool {
69 if int(id) < 0 {
70 return false
71 }
72 node := t.a.nodes[int(id)]
73 return node.kind == .ident && node.value == 'err'
74}
75
76fn (mut t Transformer) try_return_direct_optional_expr(node flat.Node) ?[]flat.NodeId {
77 if node.children_count != 1 || !t.is_optional_type_name(t.cur_fn_ret_type) {
78 return none
79 }
80 child_id := t.a.child(&node, 0)
81 child := t.a.nodes[int(child_id)]
82 if child.kind == .none_expr || !t.return_expr_is_optional_result(child_id) {
83 return none
84 }
85 ret_type := t.qualify_optional_type(t.cur_fn_ret_type)
86 expr_type := t.qualify_optional_type(t.optional_result_expr_type_name(child_id))
87 if !t.optional_types_match(ret_type, expr_type) {
88 return none
89 }
90 new_expr := t.transform_expr(child_id)
91 mut result := []flat.NodeId{}
92 t.drain_pending(mut result)
93 result << t.make_return(new_expr, ret_type)
94 return result
95}
96
97fn (t &Transformer) optional_types_match(a string, b string) bool {
98 if !t.is_optional_type_name(a) || !t.is_optional_type_name(b) || a[0] != b[0] {
99 return false
100 }
101 a_base := t.normalize_type_alias(t.optional_base_type(a))
102 b_base := t.normalize_type_alias(t.optional_base_type(b))
103 if a_base == b_base {
104 return true
105 }
106 return a_base.all_after_last('.') == b_base.all_after_last('.')
107}
108
109// try_expand_return_optional_expr
110// supports helper handling in transform.
111fn (mut t Transformer) try_expand_return_optional_expr(node flat.Node) ?[]flat.NodeId {
112 if node.children_count != 1 || !t.is_optional_type_name(t.cur_fn_ret_type) {
113 return none
114 }
115 child_id := t.a.child(&node, 0)
116 child := t.a.nodes[int(child_id)]
117 if child.kind == .none_expr {
118 return none
119 }
120 if !t.return_expr_is_optional_result(child_id) {
121 return none
122 }
123 expr_type0 := t.optional_result_expr_type_name(child_id)
124 if !t.is_optional_type_name(expr_type0) {
125 return none
126 }
127 ret_type := t.qualify_optional_type(t.cur_fn_ret_type)
128 expr_type := t.qualify_optional_type(expr_type0)
129 new_expr := t.transform_expr(child_id)
130 mut result := []flat.NodeId{}
131 t.drain_pending(mut result)
132 tmp_name := t.new_temp('return_opt')
133 result << t.make_decl_assign_typed(tmp_name, new_expr, expr_type)
134 tmp_ident := t.make_ident(tmp_name)
135 ok_cond := t.make_selector(tmp_ident, 'ok', 'bool')
136 value := t.make_selector(t.make_ident(tmp_name), 'value', t.optional_base_type(expr_type))
137 then_block := t.make_block(arr1(t.make_return(value, ret_type)))
138 else_block := t.make_block(arr1(t.make_none_return_stmt()))
139 result << t.make_if(ok_cond, then_block, else_block)
140 return result
141}
142
143// return_expr_is_optional_result supports return expr is optional result handling for Transformer.
144fn (t &Transformer) return_expr_is_optional_result(id flat.NodeId) bool {
145 if int(id) < 0 {
146 return false
147 }
148 node := t.a.nodes[int(id)]
149 if node.kind == .call && !isnil(t.tc) {
150 if name := t.tc.resolved_call_name(id) {
151 if ret := t.tc.fn_ret_types[name] {
152 return t.is_optional_type_name(ret.name())
153 }
154 }
155 return t.is_optional_type_name(t.get_call_return_type(id, node))
156 }
157 if node.kind == .ident {
158 typ := t.var_type(node.value)
159 return t.is_optional_type_name(typ) || t.is_optional_type_name(node.typ)
160 }
161 if node.kind in [.selector, .index, .if_expr, .match_stmt, .or_expr] {
162 return t.is_optional_type_name(t.node_type(id))
163 }
164 return t.is_optional_type_name(node.typ)
165}
166
167// return_block_from_branch builds a block that keeps leading statements
168// (transformed) and turns the tail expression of the branch into a `return`.
169fn (mut t Transformer) return_block_from_branch(branch_id flat.NodeId, ret_typ string) flat.NodeId {
170 branch := t.a.nodes[int(branch_id)]
171 if branch.kind == .return_stmt {
172 mut all := []flat.NodeId{}
173 ret_stmts := t.transform_stmt(branch_id)
174 t.drain_pending(mut all)
175 for stmt in ret_stmts {
176 all << stmt
177 }
178 return t.make_block(all)
179 }
180 if branch.kind != .block {
181 // single expression branch: just `return <expr>`
182 mut all := []flat.NodeId{}
183 // Convert a fixed-array branch value (e.g. a fixed-array const) to a dynamic
184 // array when the function returns `[]T`, matching a plain `return <expr>` and
185 // the `return match {...}` path (match_branch_return_block).
186 ret_val := if converted := t.fixed_array_return_value(branch_id) {
187 converted
188 } else {
189 t.wrap_sum_return_expr(branch_id)
190 }
191 t.drain_pending(mut all)
192 all << t.make_return(ret_val, 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_val := if converted := t.fixed_array_return_value(tail_expr) {
221 converted
222 } else {
223 t.wrap_sum_return_expr(tail_expr)
224 }
225 t.drain_pending(mut all)
226 ret := t.make_return(ret_val, ret_typ)
227 all << ret
228 return t.make_block(all)
229}
230
231// build_return_if_chain recursively converts an if_expr (possibly an else-if
232// chain) into an if-statement whose branch tails are `return` statements.
233fn (mut t Transformer) build_return_if_chain(if_id flat.NodeId, ret_typ string) flat.NodeId {
234 if_node := t.a.nodes[int(if_id)]
235 cond_id := t.a.child(&if_node, 0)
236 cond_smartcasts := t.extract_all_is_exprs(cond_id)
237 new_cond := t.transform_and_chain_smartcasts(cond_id)
238 then_id := t.a.child(&if_node, 1)
239 for info in cond_smartcasts {
240 t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
241 }
242 then_block := t.return_block_from_branch(then_id, ret_typ)
243 for _ in cond_smartcasts {
244 t.pop_smartcast()
245 }
246 mut else_block := flat.empty_node
247 if if_node.children_count >= 3 {
248 else_id := t.a.child(&if_node, 2)
249 else_node := t.a.nodes[int(else_id)]
250 if else_node.kind == .if_expr {
251 // else-if chain: recurse, wrap resulting if-stmt in a block
252 inner := t.build_return_if_chain(else_id, ret_typ)
253 else_block = t.make_block(arr1(inner))
254 } else {
255 else_block = t.return_block_from_branch(else_id, ret_typ)
256 }
257 }
258 return t.make_if(new_cond, then_block, else_block)
259}
260
261// try_expand_return_if detects a `return if cond { a } else { b }` pattern
262// and expands it into `if cond { return a } else { return b }`.
263// This simplification makes the C backend's job easier since it avoids
264// needing statement-expressions for the if-as-value in the return position.
265//
266// Leading statements in each branch are preserved, and a trailing `.expr_stmt`
267// is unwrapped so the inner expression (not the statement) is returned.
268//
269// Returns the expanded if-statement as a single-element array, or none if the
270// pattern does not match. A plain `return if x {..}` with no else (if_expr with
271// fewer than 3 children) is left unexpanded.
272fn (mut t Transformer) try_expand_return_if(_id flat.NodeId, node flat.Node) ?[]flat.NodeId {
273 if node.children_count == 0 {
274 return none
275 }
276 val_id := t.a.child(&node, 0)
277 if int(val_id) < 0 || int(val_id) >= t.a.nodes.len {
278 return none
279 }
280 val_node := t.a.nodes[int(val_id)]
281 if val_node.kind != .if_expr || val_node.children_count < 3 {
282 return none
283 }
284 return arr1(t.build_return_if_chain(val_id, node.typ))
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