1use smol_str::SmolStr;
5use std::collections::BTreeMap;
6use std::sync::Arc;
7
8use crate::expression_tree::Expression;
9use crate::langtype::{Struct, StructName, Type};
10use crate::symbol_counters::SymbolCounters;
11
12pub fn remove_return(doc: &crate::object_tree::Document, symbol_counters: &SymbolCounters) {
13 doc.visit_all_used_components(|component| {
14 crate::object_tree::visit_all_expressions(component, |e, _| {
15 let mut ret_ty = None;
16 fn visit(e: &Expression, ret_ty: &mut Option<Type>) {
17 if ret_ty.is_some() {
18 return;
19 }
20 match e {
21 Expression::ReturnStatement(x) => {
22 *ret_ty = Some(x.as_ref().map_or(Type::Void, |x| x.ty()));
23 }
24 _ => e.visit(|e| visit(e, ret_ty)),
25 };
26 }
27 visit(e, &mut ret_ty);
28 let Some(ret_ty) = ret_ty else { return };
29 let ctx = RemoveReturnContext { ret_ty };
30 *e = process_expression(std::mem::take(e), true, &ctx, &ctx.ret_ty, symbol_counters)
31 .into_expression(&ctx.ret_ty, symbol_counters);
32 })
33 });
34}
35
36fn process_expression(
37 e: Expression,
38 toplevel: bool,
39 ctx: &RemoveReturnContext,
40 ty: &Type,
41 symbol_counters: &SymbolCounters,
42) -> ExpressionResult {
43 match e {
44 Expression::DebugHook { expression, .. } => {
45 process_expression(*expression, toplevel, ctx, ty, symbol_counters)
46 }
47 Expression::ReturnStatement(expr) => ExpressionResult::Return(expr.map(|e| *e)),
48 Expression::CodeBlock(expr) => {
49 process_codeblock(expr.into_iter().peekable(), toplevel, ty, ctx, symbol_counters)
50 }
51 Expression::Condition { condition, true_expr, false_expr, .. } => {
52 process_condition(condition, *true_expr, *false_expr, ctx, ty, symbol_counters)
53 }
54 Expression::Cast { from, to } => {
55 let ty = if !has_value(ty) { ty.clone() } else { from.ty() };
56 process_expression(*from, toplevel, ctx, &ty, symbol_counters)
57 .map_value(symbol_counters, |e| Expression::Cast { from: e.into(), to })
58 }
59 Expression::StoreLocalVariable { name, value } => {
60 process_store_local_variable(name, *value, ctx, symbol_counters)
61 }
62 e => {
63 #[cfg(debug_assertions)]
65 {
66 e.visit_recursive(&mut |e| assert!(!matches!(e, Expression::ReturnStatement(_))));
67 }
68 ExpressionResult::Just(e)
69 }
70 }
71}
72
73fn process_condition(
74 condition: Box<Expression>,
75 true_expr: Expression,
76 false_expr: Expression,
77 ctx: &RemoveReturnContext,
78 ty: &Type,
79 symbol_counters: &SymbolCounters,
80) -> ExpressionResult {
81 let te = process_expression(true_expr, false, ctx, ty, symbol_counters);
82 let fe = process_expression(false_expr, false, ctx, ty, symbol_counters);
83 merge_condition_branches(condition, te, fe, ctx, ty, symbol_counters)
84}
85
86fn merge_condition_branches(
87 condition: Box<Expression>,
88 te: ExpressionResult,
89 fe: ExpressionResult,
90 ctx: &RemoveReturnContext,
91 ty: &Type,
92 symbol_counters: &SymbolCounters,
93) -> ExpressionResult {
94 match (te, fe) {
95 (ExpressionResult::Just(te), ExpressionResult::Just(fe)) => Expression::Condition {
96 condition,
97 true_expr: te.into(),
98 false_expr: fe.into(),
99 source_location: None,
100 }
101 .into(),
102 (ExpressionResult::Just(te), ExpressionResult::Return(fe)) => {
103 ExpressionResult::MaybeReturn {
104 pre_statements: Vec::new(),
105 condition: *condition,
106 returned_value: fe,
107 actual_value: cleanup_empty_block(te),
108 }
109 }
110 (ExpressionResult::Return(te), ExpressionResult::Just(fe)) => {
111 ExpressionResult::MaybeReturn {
112 pre_statements: Vec::new(),
113 condition: Expression::UnaryOp { sub: condition, op: '!' },
114 returned_value: te,
115 actual_value: cleanup_empty_block(fe),
116 }
117 }
118 (ExpressionResult::Return(te), ExpressionResult::Return(fe)) => {
119 ExpressionResult::Return(Some(Expression::Condition {
120 condition,
121 true_expr: te.unwrap_or(Expression::CodeBlock(Vec::new())).into(),
122 false_expr: fe.unwrap_or(Expression::CodeBlock(Vec::new())).into(),
123 source_location: None,
124 }))
125 }
126 (te, fe) => {
127 let has_value = has_value(ty) && (te.has_value() || fe.has_value());
128 let ty = if has_value { ty } else { &Type::Void };
129 let te = te.into_return_object(ty, &ctx.ret_ty, symbol_counters);
130 let fe = fe.into_return_object(ty, &ctx.ret_ty, symbol_counters);
131 ExpressionResult::ReturnObject {
132 has_value,
133 has_return_value: self::has_value(&ctx.ret_ty),
134 value: Expression::Condition {
135 condition,
136 true_expr: te.into(),
137 false_expr: fe.into(),
138 source_location: None,
139 },
140 }
141 }
142 }
143}
144
145fn process_store_local_variable(
146 name: SmolStr,
147 value: Expression,
148 ctx: &RemoveReturnContext,
149 symbol_counters: &SymbolCounters,
150) -> ExpressionResult {
151 let inner_ty = value.ty();
152 match process_expression(value, false, ctx, &inner_ty, symbol_counters) {
153 ExpressionResult::Just(e) => {
154 ExpressionResult::Just(Expression::StoreLocalVariable { name, value: Box::new(e) })
155 }
156 ExpressionResult::Return(r) => ExpressionResult::Return(r),
157 ExpressionResult::MaybeReturn {
158 pre_statements,
159 condition,
160 returned_value,
161 actual_value,
162 } => ExpressionResult::MaybeReturn {
163 pre_statements,
164 condition,
165 returned_value,
166 actual_value: Some(Expression::StoreLocalVariable {
167 name,
168 value: Box::new(
169 actual_value.unwrap_or(Expression::default_value_for_type(&inner_ty)),
170 ),
171 }),
172 },
173 ExpressionResult::ReturnObject { value, has_return_value, .. } => {
174 let tmp_name: SmolStr = symbol_counters.generate_name("return_check_store");
175 let value_ty = value.ty();
176 let load = |field: &str| Expression::StructFieldAccess {
177 base: Box::new(Expression::ReadLocalVariable {
178 name: tmp_name.clone(),
179 ty: value_ty.clone(),
180 }),
181 name: field.into(),
182 };
183 let condition = load(FIELD_CONDITION);
184 let returned_value = has_return_value.then(|| load(FIELD_RETURNED));
185 let actual_value =
186 Some(Expression::StoreLocalVariable { name, value: Box::new(load(FIELD_ACTUAL)) });
187 ExpressionResult::MaybeReturn {
188 pre_statements: vec![Expression::StoreLocalVariable {
189 name: tmp_name,
190 value: Box::new(value),
191 }],
192 condition,
193 returned_value,
194 actual_value,
195 }
196 }
197 }
198}
199
200fn cleanup_empty_block(te: Expression) -> Option<Expression> {
202 if matches!(&te, Expression::CodeBlock(stmts) if stmts.is_empty()) { None } else { Some(te) }
203}
204
205fn process_codeblock(
206 mut iter: std::iter::Peekable<impl Iterator<Item = Expression>>,
207 toplevel: bool,
208 ty: &Type,
209 ctx: &RemoveReturnContext,
210 symbol_counters: &SymbolCounters,
211) -> ExpressionResult {
212 let mut stmts = Vec::new();
213 while let Some(e) = iter.next() {
214 let is_last = iter.peek().is_none();
215 match process_expression(
216 e,
217 toplevel,
218 ctx,
219 if is_last { ty } else { &Type::Void },
220 symbol_counters,
221 ) {
222 ExpressionResult::Just(x) => stmts.push(x),
223 ExpressionResult::Return(x) => {
224 stmts.extend(x);
225 return ExpressionResult::Return(
226 (!stmts.is_empty()).then_some(Expression::CodeBlock(stmts)),
227 );
228 }
229 ExpressionResult::MaybeReturn {
230 mut pre_statements,
231 condition,
232 returned_value,
233 actual_value,
234 } => {
235 stmts.append(&mut pre_statements);
236 if is_last {
237 return ExpressionResult::MaybeReturn {
238 pre_statements: stmts,
239 condition,
240 returned_value,
241 actual_value,
242 };
243 } else if toplevel {
244 let rest = process_codeblock(iter, true, ty, ctx, symbol_counters)
245 .into_expression(&ctx.ret_ty, symbol_counters);
246 let mut rest_ex = Expression::CodeBlock(
247 actual_value.into_iter().chain(core::iter::once(rest)).collect(),
248 );
249 if rest_ex.ty() != ctx.ret_ty {
250 rest_ex =
251 Expression::Cast { from: Box::new(rest_ex), to: ctx.ret_ty.clone() }
252 }
253 return ExpressionResult::MaybeReturn {
254 pre_statements: stmts,
255 condition,
256 returned_value,
257 actual_value: Some(rest_ex),
258 };
259 } else {
260 return continue_codeblock(
261 iter,
262 ty,
263 ctx,
264 ExpressionResult::MaybeReturn {
265 pre_statements: Vec::new(),
266 condition,
267 returned_value,
268 actual_value,
269 }
270 .into_return_object(
271 ty,
272 &ctx.ret_ty,
273 symbol_counters,
274 ),
275 stmts,
276 has_value(&ctx.ret_ty),
277 symbol_counters,
278 );
279 }
280 }
281 ExpressionResult::ReturnObject { value, has_value, has_return_value } => {
282 if is_last {
283 return ExpressionResult::ReturnObject {
284 value: codeblock_with_expr(stmts, value),
285 has_value,
286 has_return_value,
287 };
288 } else {
289 return continue_codeblock(
290 iter,
291 ty,
292 ctx,
293 value,
294 stmts,
295 has_return_value,
296 symbol_counters,
297 );
298 }
299 }
300 }
301 }
302 ExpressionResult::Just(Expression::CodeBlock(stmts))
303}
304
305fn continue_codeblock(
306 iter: std::iter::Peekable<impl Iterator<Item = Expression>>,
307 ty: &Type,
308 ctx: &RemoveReturnContext,
309 return_object: Expression,
310 mut stmts: Vec<Expression>,
311 has_return_value: bool,
312 symbol_counters: &SymbolCounters,
313) -> ExpressionResult {
314 let rest = process_codeblock(iter, false, ty, ctx, symbol_counters).into_return_object(
315 ty,
316 &ctx.ret_ty,
317 symbol_counters,
318 );
319 let unique_name = symbol_counters.generate_name("return_check_merge");
320 let load = Box::new(Expression::ReadLocalVariable {
321 name: unique_name.clone(),
322 ty: return_object.ty(),
323 });
324 stmts.push(Expression::StoreLocalVariable { name: unique_name, value: return_object.into() });
325 stmts.push(Expression::Condition {
326 condition: Expression::StructFieldAccess {
327 base: load.clone(),
328 name: FIELD_CONDITION.into(),
329 }
330 .into(),
331 true_expr: rest.into(),
332 false_expr: ExpressionResult::Return(has_return_value.then(|| {
333 Expression::StructFieldAccess { base: load.clone(), name: FIELD_RETURNED.into() }
334 }))
335 .into_return_object(ty, &ctx.ret_ty, symbol_counters)
336 .into(),
337 source_location: None,
338 });
339 ExpressionResult::ReturnObject {
340 value: Expression::CodeBlock(stmts),
341 has_value: has_value(ty),
342 has_return_value,
343 }
344}
345
346struct RemoveReturnContext {
347 ret_ty: Type,
348}
349
350#[derive(Debug)]
351#[allow(clippy::large_enum_variant)]
352enum ExpressionResult {
353 Just(Expression),
355 MaybeReturn {
357 pre_statements: Vec<Expression>,
359 condition: Expression,
361 returned_value: Option<Expression>,
363 actual_value: Option<Expression>,
365 },
366 Return(Option<Expression>),
368 ReturnObject { value: Expression, has_value: bool, has_return_value: bool },
371}
372
373impl From<Expression> for ExpressionResult {
374 fn from(v: Expression) -> Self {
375 Self::Just(v)
376 }
377}
378
379const FIELD_CONDITION: &str = "condition";
380const FIELD_ACTUAL: &str = "actual";
381const FIELD_RETURNED: &str = "returned";
382
383impl ExpressionResult {
384 fn into_expression(self, ty: &Type, symbol_counters: &SymbolCounters) -> Expression {
385 match self {
386 ExpressionResult::Just(e) => e,
387 ExpressionResult::Return(e) => e.unwrap_or(Expression::CodeBlock(Vec::new())),
388 ExpressionResult::MaybeReturn {
389 mut pre_statements,
390 condition,
391 returned_value,
392 actual_value,
393 } => {
394 pre_statements.push(Expression::Condition {
395 condition: condition.into(),
396 true_expr: actual_value.unwrap_or(Expression::CodeBlock(Vec::new())).into(),
397 false_expr: returned_value.unwrap_or(Expression::CodeBlock(Vec::new())).into(),
398 source_location: None,
399 });
400 Expression::CodeBlock(pre_statements)
401 }
402 ExpressionResult::ReturnObject { value, has_value, has_return_value } => {
403 let name = symbol_counters.generate_name("returned_expression");
404 let load =
405 Box::new(Expression::ReadLocalVariable { name: name.clone(), ty: value.ty() });
406 Expression::CodeBlock(vec![
407 Expression::StoreLocalVariable { name, value: value.into() },
408 Expression::Condition {
409 condition: Expression::StructFieldAccess {
410 base: load.clone(),
411 name: FIELD_CONDITION.into(),
412 }
413 .into(),
414 true_expr: if has_value {
415 Expression::StructFieldAccess {
416 base: load.clone(),
417 name: FIELD_ACTUAL.into(),
418 }
419 } else {
420 Expression::default_value_for_type(ty)
421 }
422 .into(),
423 false_expr: if has_return_value {
424 Expression::StructFieldAccess {
425 base: load.clone(),
426 name: FIELD_RETURNED.into(),
427 }
428 } else {
429 Expression::default_value_for_type(ty)
430 }
431 .into(),
432 source_location: None,
433 },
434 ])
435 }
436 }
437 }
438
439 fn into_return_object(
440 self,
441 ty: &Type,
442 ret_ty: &Type,
443 symbol_counters: &SymbolCounters,
444 ) -> Expression {
445 match self {
446 ExpressionResult::Just(e) => {
447 let ret_value = Expression::default_value_for_type(ret_ty);
448 if has_value(ty) {
449 make_struct(
450 [
451 (FIELD_CONDITION, Type::Bool, Expression::BoolLiteral(true)),
452 (FIELD_RETURNED, ret_ty.clone(), ret_value),
453 (FIELD_ACTUAL, e.ty(), e),
454 ]
455 .into_iter(),
456 )
457 } else {
458 let object = make_struct(
459 [
460 (FIELD_CONDITION, Type::Bool, Expression::BoolLiteral(true)),
461 (FIELD_RETURNED, ret_ty.clone(), ret_value),
462 ]
463 .into_iter(),
464 );
465 if e.is_constant(None) {
466 object
467 } else {
468 Expression::CodeBlock(vec![e, object])
469 }
470 }
471 }
472 ExpressionResult::MaybeReturn {
473 pre_statements,
474 condition,
475 returned_value,
476 actual_value,
477 } => {
478 let mut true_expr = match actual_value {
479 Some(e) => {
480 ExpressionResult::Just(e).into_return_object(ty, ret_ty, symbol_counters)
481 }
482 None => make_struct(
483 [(FIELD_CONDITION, Type::Bool, Expression::BoolLiteral(true))].into_iter(),
484 ),
485 };
486 let mut false_expr = ExpressionResult::Return(returned_value).into_return_object(
487 ty,
488 ret_ty,
489 symbol_counters,
490 );
491 let true_ty = true_expr.ty();
492 let false_ty = false_expr.ty();
493 if true_ty != false_ty {
494 let common_ty = Expression::common_target_type_for_type_list(
495 [&true_ty, &false_ty].into_iter().cloned(),
496 );
497 if common_ty != true_ty {
498 true_expr = convert_struct(
499 std::mem::take(&mut true_expr),
500 common_ty.clone(),
501 symbol_counters,
502 )
503 }
504 if common_ty != false_ty {
505 false_expr = convert_struct(
506 std::mem::take(&mut false_expr),
507 common_ty,
508 symbol_counters,
509 )
510 }
511 }
512 let o = Expression::Condition {
513 condition: condition.into(),
514 true_expr: true_expr.into(),
515 false_expr: false_expr.into(),
516 source_location: None,
517 };
518 codeblock_with_expr(pre_statements, o)
519 }
520 ExpressionResult::Return(r) => make_struct(
521 [(FIELD_CONDITION, Type::Bool, Expression::BoolLiteral(false))]
522 .into_iter()
523 .chain(r.map(|r| (FIELD_RETURNED, ret_ty.clone(), r)))
524 .chain(has_value(ty).then(|| {
525 (FIELD_ACTUAL, ty.clone(), Expression::default_value_for_type(ty))
526 })),
527 ),
528 ExpressionResult::ReturnObject { value, .. } => value,
529 }
530 }
531
532 fn map_value(
533 self,
534 symbol_counters: &SymbolCounters,
535 f: impl FnOnce(Expression) -> Expression,
536 ) -> Self {
537 match self {
538 ExpressionResult::Just(e) => ExpressionResult::Just(f(e)),
539 ExpressionResult::Return(e) => ExpressionResult::Return(e),
540 ExpressionResult::MaybeReturn {
541 pre_statements,
542 condition,
543 returned_value,
544 actual_value,
545 } => ExpressionResult::MaybeReturn {
546 pre_statements,
547 condition,
548 returned_value,
549 actual_value: actual_value.map(f),
550 },
551 ExpressionResult::ReturnObject { value, has_value, has_return_value } => {
552 if !has_value {
553 return ExpressionResult::ReturnObject { value, has_value, has_return_value };
554 }
555 let name = symbol_counters.generate_name("mapped_expression");
556 let value_ty = value.ty();
557 let load = |field: &str| Expression::StructFieldAccess {
558 base: Box::new(Expression::ReadLocalVariable {
559 name: name.clone(),
560 ty: value_ty.clone(),
561 }),
562 name: field.into(),
563 };
564 let condition = (FIELD_CONDITION, Type::Bool, load(FIELD_CONDITION));
565 let actual = f(load(FIELD_ACTUAL));
566 let actual = (FIELD_ACTUAL, actual.ty(), actual);
567 let ret = has_return_value.then(|| {
568 let r = load(FIELD_RETURNED);
569 (FIELD_RETURNED, r.ty(), r)
570 });
571 ExpressionResult::ReturnObject {
572 value: Expression::CodeBlock(vec![
573 Expression::StoreLocalVariable { name, value: value.into() },
574 make_struct([condition, actual].into_iter().chain(ret.into_iter())),
575 ]),
576 has_value,
577 has_return_value,
578 }
579 }
580 }
581 }
582
583 fn has_value(&self) -> bool {
584 match self {
585 ExpressionResult::Just(expression) => has_value(&expression.ty()),
586 ExpressionResult::MaybeReturn { actual_value, .. } => {
587 actual_value.as_ref().is_some_and(|x| has_value(&x.ty()))
588 }
589 ExpressionResult::Return(..) => false,
590 ExpressionResult::ReturnObject { has_value, .. } => *has_value,
591 }
592 }
593}
594
595fn codeblock_with_expr(mut pre_statements: Vec<Expression>, expr: Expression) -> Expression {
596 if pre_statements.is_empty() {
597 expr
598 } else {
599 pre_statements.push(expr);
600 Expression::CodeBlock(pre_statements)
601 }
602}
603
604fn make_struct(it: impl Iterator<Item = (&'static str, Type, Expression)>) -> Expression {
605 let mut fields = BTreeMap::<SmolStr, Type>::new();
606 let mut values = BTreeMap::<SmolStr, Expression>::new();
607 let mut voids = Vec::new();
608 for (name, ty, expr) in it {
609 if !has_value(&ty) {
610 if ty != Type::Invalid {
611 voids.push(expr);
612 }
613 continue;
614 }
615 fields.insert(name.into(), ty);
616 values.insert(name.into(), expr);
617 }
618 codeblock_with_expr(
619 voids,
620 Expression::Struct { ty: Arc::new(Struct::new(fields, StructName::None)), values },
621 )
622}
623
624fn convert_struct(from: Expression, to: Type, symbol_counters: &SymbolCounters) -> Expression {
627 let Type::Struct(to) = to else {
628 assert_eq!(to, Type::Invalid);
629 return Expression::Invalid;
630 };
631 if let Expression::Struct { mut values, .. } = from {
632 let mut new_values = BTreeMap::new();
633 for key in to.fields.keys() {
634 let (key, expression) = values
635 .remove_entry(key)
636 .unwrap_or_else(|| (key.clone(), to.default_value_for_field(key)));
637 new_values.insert(key, expression);
638 }
639 return Expression::Struct { values: new_values, ty: to };
640 }
641 let var_name = symbol_counters.generate_name("tmpobj_ret_conv_");
642 let from_ty = from.ty();
643 let mut new_values = BTreeMap::new();
644 let Type::Struct(from_s) = &from_ty else {
645 assert_eq!(from_ty, Type::Invalid);
646 return Expression::Invalid;
647 };
648 for key in to.fields.keys() {
649 let expression = if from_s.fields.contains_key(key) {
650 Expression::StructFieldAccess {
651 base: Box::new(Expression::ReadLocalVariable {
652 name: var_name.clone(),
653 ty: from_ty.clone(),
654 }),
655 name: key.clone(),
656 }
657 } else {
658 to.default_value_for_field(key)
659 };
660 new_values.insert(key.clone(), expression);
661 }
662 Expression::CodeBlock(vec![
663 Expression::StoreLocalVariable { name: var_name, value: Box::new(from) },
664 Expression::Struct { values: new_values, ty: to },
665 ])
666}
667
668fn has_value(ty: &Type) -> bool {
669 !matches!(ty, Type::Void | Type::Invalid)
670}