luaur_analysis/methods/cfg_builder_resolve_condition.rs
1//! Source: `Analysis/src/ControlFlowGraph.cpp:368-422` (hand-ported)
2//! C++ `std::optional<RefinementId> CFGBuilder::resolveCondition(AstExpr* condition)`.
3use crate::functions::match_type_guard::match_type_guard;
4use crate::methods::refinement_arena_type_proposition::refinement_arena_type_proposition;
5use crate::records::cfg_builder::CfgBuilder;
6use crate::records::symbol::Symbol;
7use crate::type_aliases::def_id_control_flow_graph::DefId;
8use crate::type_aliases::refinement_id_control_flow_graph::RefinementId;
9use alloc::string::ToString;
10use luaur_ast::records::ast_expr::AstExpr;
11use luaur_ast::records::ast_expr_binary::AstExprBinary;
12use luaur_ast::records::ast_expr_group::AstExprGroup;
13use luaur_ast::records::ast_expr_local::AstExprLocal;
14use luaur_ast::records::ast_expr_unary::AstExprUnary;
15use luaur_ast::records::ast_node::AstNode;
16use luaur_ast::rtti::ast_node_as;
17
18impl CfgBuilder {
19 pub fn resolve_condition(&mut self, condition: *mut AstExpr) -> Option<RefinementId> {
20 unsafe {
21 // auto& arena = allocator->refinementArena; (accessed per-use below)
22 let node = condition as *mut AstNode;
23
24 // if (auto group = condition->as<AstExprGroup>())
25 // return resolveCondition(group->expr);
26 let group = ast_node_as::<AstExprGroup>(node);
27 if !group.is_null() {
28 return self.resolve_condition((*group).expr);
29 }
30
31 // else if (auto loc = condition->as<AstExprLocal>()) { ... }
32 let loc = ast_node_as::<AstExprLocal>(node);
33 if !loc.is_null() {
34 // DefId def = readVariable(currentBlock, Symbol(loc->local));
35 let def: DefId =
36 self.read_variable(self.current_block, Symbol::from_local((*loc).local));
37 // cfg->useDefs[loc] = def;
38 *self
39 .cfg
40 .as_mut()
41 .unwrap()
42 .use_defs
43 .get_or_insert(loc as *mut AstExpr) = def;
44 // return arena.proposition(def, /* sense */ true);
45 return Some(
46 (*self.allocator)
47 .refinement_arena
48 .proposition_def_id_bool(def, true),
49 );
50 }
51
52 // else if (auto binop = condition->as<AstExprBinary>()) { ... }
53 let binop = ast_node_as::<AstExprBinary>(node);
54 if !binop.is_null() {
55 let op = (*binop).op;
56 // if (auto tg = matchTypeGuard(binop->op, binop->left, binop->right)) { ... }
57 if let Some(tg) = match_type_guard(op as i32, (*binop).left, (*binop).right) {
58 // if (auto tgtLocal = tg->target->as<AstExprLocal>()) { ... }
59 let tgt_local = ast_node_as::<AstExprLocal>(tg.target() as *mut AstNode);
60 if !tgt_local.is_null() {
61 // auto def = readVariable(currentBlock, Symbol(tgtLocal->local));
62 let def = self.read_variable(
63 self.current_block,
64 Symbol::from_local((*tgt_local).local),
65 );
66 // cfg->useDefs[tgtLocal] = def;
67 *self
68 .cfg
69 .as_mut()
70 .unwrap()
71 .use_defs
72 .get_or_insert(tgt_local as *mut AstExpr) = def;
73 // bool sense = binop->op == AstExprBinary::CompareEq;
74 let sense = op == AstExprBinary::CompareEq;
75 // return arena.typeProposition(def, tg->type, tg->isTypeof, sense);
76 let arena = &mut (*self.allocator).refinement_arena;
77 return Some(refinement_arena_type_proposition(
78 arena,
79 def,
80 Some(tg.r#type().to_string()),
81 tg.isTypeof(),
82 sense,
83 ));
84 }
85 // return std::nullopt;
86 return None;
87 }
88
89 // auto lRef = resolveCondition(binop->left);
90 // auto rRef = resolveCondition(binop->right);
91 let l_ref = self.resolve_condition((*binop).left);
92 let r_ref = self.resolve_condition((*binop).right);
93 if op == AstExprBinary::And {
94 // (A and B) truthy => both truthy; a missing side still preserves the other.
95 if let (Some(l), Some(r)) = (l_ref, r_ref) {
96 return Some((*self.allocator).refinement_arena.conjunction_mut(l, r));
97 }
98 return if l_ref.is_some() { l_ref } else { r_ref };
99 } else if op == AstExprBinary::Or {
100 // (A or B) truthy => at least one truthy; an unrefined side means we can't narrow.
101 if let (Some(l), Some(r)) = (l_ref, r_ref) {
102 return Some((*self.allocator).refinement_arena.disjunction_mut(l, r));
103 }
104 }
105 // falls through to `return std::nullopt;`
106 return None;
107 }
108
109 // else if (auto unop = condition->as<AstExprUnary>()) { ... }
110 let unop = ast_node_as::<AstExprUnary>(node);
111 if !unop.is_null() {
112 // if (unop->op == AstExprUnary::Not)
113 if (*unop).op == AstExprUnary::Not {
114 // if (auto inner = resolveCondition(unop->expr))
115 // return arena.negation(*inner);
116 if let Some(inner) = self.resolve_condition((*unop).expr) {
117 return Some((*self.allocator).refinement_arena.negation_mut(inner));
118 }
119 }
120 }
121
122 // return std::nullopt;
123 None
124 }
125 }
126}