Skip to main content

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}