Skip to main content

luaur_analysis/methods/
constraint_generator_compute_refinement.rs

1use crate::enums::table_state::TableState;
2use crate::functions::contains_subscripted_definition::contains_subscripted_definition;
3use crate::functions::follow_type::follow_type_id;
4use crate::functions::get_type_alt_j::get_type_id;
5use crate::records::conjunction_refinement::Conjunction;
6use crate::records::constraint_generator::ConstraintGenerator;
7use crate::records::disjunction_refinement::Disjunction;
8use crate::records::equivalence::Equivalence;
9use crate::records::negation_refinement::Negation;
10use crate::records::negation_type::NegationType;
11use crate::records::property_type::Property;
12use crate::records::proposition_refinement::Proposition;
13use crate::records::refinement_partition::RefinementPartition;
14use crate::records::scope::Scope;
15use crate::records::table_type::TableType;
16use crate::records::variadic::Variadic;
17use crate::type_aliases::constraint_v::ConstraintV;
18use crate::type_aliases::def_id_def::DefId;
19use crate::type_aliases::name_type::Name;
20use crate::type_aliases::refinement_context::RefinementContext;
21use crate::type_aliases::refinement_id_refinement::RefinementId;
22use crate::type_aliases::refinement_refinement::{Refinement, RefinementMember};
23use alloc::collections::BTreeMap;
24use core::mem::ManuallyDrop;
25use luaur_ast::records::location::Location;
26use luaur_common::macros::luau_assert::LUAU_ASSERT;
27
28impl ConstraintGenerator {
29    // ConstraintGenerator::computeRefinement(const ScopePtr&, Location, RefinementId,
30    //     RefinementContext*, bool sense, bool eq, std::vector<ConstraintV>*)
31    // (ConstraintGenerator.cpp:565).
32    pub fn compute_refinement(
33        &mut self,
34        scope: *mut Scope,
35        location: Location,
36        refinement: RefinementId,
37        refis: *mut RefinementContext,
38        sense: bool,
39        eq: bool,
40        constraints: *mut alloc::vec::Vec<ConstraintV>,
41    ) {
42        if refinement.is_null() {
43            return;
44        }
45
46        let refinement_ref: &Refinement = unsafe { &*refinement };
47
48        if let Some(variadic) = <Variadic as RefinementMember>::get_if(refinement_ref) {
49            for refi in variadic.refinements.clone() {
50                self.compute_refinement(scope, location, refi, refis, sense, eq, constraints);
51            }
52        } else if let Some(negation) = <Negation as RefinementMember>::get_if(refinement_ref) {
53            self.compute_refinement(
54                scope,
55                location,
56                negation.refinement,
57                refis,
58                !sense,
59                eq,
60                constraints,
61            );
62        } else if let Some(conjunction) = <Conjunction as RefinementMember>::get_if(refinement_ref)
63        {
64            let (lhs, rhs) = (conjunction.lhs, conjunction.rhs);
65            let mut lhs_refis = RefinementContext::default();
66            let mut rhs_refis = RefinementContext::default();
67
68            let lhs_target: *mut RefinementContext = if sense { refis } else { &mut lhs_refis };
69            self.compute_refinement(scope, location, lhs, lhs_target, sense, eq, constraints);
70            let rhs_target: *mut RefinementContext = if sense { refis } else { &mut rhs_refis };
71            self.compute_refinement(scope, location, rhs, rhs_target, sense, eq, constraints);
72
73            if !sense {
74                let sp =
75                    ManuallyDrop::new(unsafe { alloc::sync::Arc::from_raw(scope as *const Scope) });
76                self.union_refinements(&sp, location, &lhs_refis, &rhs_refis, refis, constraints);
77            }
78        } else if let Some(disjunction) = <Disjunction as RefinementMember>::get_if(refinement_ref)
79        {
80            let (lhs, rhs) = (disjunction.lhs, disjunction.rhs);
81            let mut lhs_refis = RefinementContext::default();
82            let mut rhs_refis = RefinementContext::default();
83
84            let lhs_target: *mut RefinementContext = if sense { &mut lhs_refis } else { refis };
85            self.compute_refinement(scope, location, lhs, lhs_target, sense, eq, constraints);
86            let rhs_target: *mut RefinementContext = if sense { &mut rhs_refis } else { refis };
87            self.compute_refinement(scope, location, rhs, rhs_target, sense, eq, constraints);
88
89            if sense {
90                let sp =
91                    ManuallyDrop::new(unsafe { alloc::sync::Arc::from_raw(scope as *const Scope) });
92                self.union_refinements(&sp, location, &lhs_refis, &rhs_refis, refis, constraints);
93            }
94        } else if let Some(equivalence) = <Equivalence as RefinementMember>::get_if(refinement_ref)
95        {
96            let (lhs, rhs) = (equivalence.lhs, equivalence.rhs);
97            self.compute_refinement(scope, location, lhs, refis, sense, true, constraints);
98            self.compute_refinement(scope, location, rhs, refis, sense, true, constraints);
99        } else if let Some(proposition) = <Proposition as RefinementMember>::get_if(refinement_ref)
100        {
101            let mut discriminant_ty = proposition.discriminantTy;
102            let prop_key = proposition.key;
103            let implicit_from_call = proposition.implicitFromCall;
104
105            // if we have a negative sense, then we need to negate the discriminant
106            if !sense {
107                let nt = unsafe { get_type_id::<NegationType>(follow_type_id(discriminant_ty)) };
108                if !nt.is_null() {
109                    discriminant_ty = unsafe { (*nt).ty };
110                } else {
111                    discriminant_ty = unsafe {
112                        (*self.arena).add_type(NegationType {
113                            ty: discriminant_ty,
114                        })
115                    };
116                }
117            }
118
119            if eq {
120                let sp =
121                    ManuallyDrop::new(unsafe { alloc::sync::Arc::from_raw(scope as *const Scope) });
122                let singleton_func = unsafe { &(*self.builtin_types).typeFunctions.singleton_func };
123                discriminant_ty = self.create_type_function_instance(
124                    singleton_func,
125                    alloc::vec![discriminant_ty],
126                    alloc::vec![],
127                    &sp,
128                    location,
129                );
130            }
131
132            let mut key = prop_key;
133            while !key.is_null() {
134                let key_def = unsafe { (*key).def } as DefId;
135
136                unsafe {
137                    (*refis).insert(key_def, RefinementPartition::default());
138                    (*refis)
139                        .get_mut(&key_def)
140                        .unwrap()
141                        .discriminant_types
142                        .push(discriminant_ty);
143                }
144
145                // Reached leaf node
146                let prop_name = unsafe { (*key).propName.clone() };
147                let prop_name = match prop_name {
148                    Some(n) => n,
149                    None => break,
150                };
151
152                let mut props: BTreeMap<Name, Property> = BTreeMap::new();
153                props.insert(prop_name, Property::readonly(discriminant_ty));
154
155                let next_discriminant_ty = unsafe {
156                    let tt = TableType::table_type_props_optional_table_indexer_type_level_scope_table_state(
157                        &props,
158                        None,
159                        (*scope).level,
160                        scope,
161                        TableState::Sealed,
162                    );
163                    (*self.arena).add_type(tt)
164                };
165
166                discriminant_ty = next_discriminant_ty;
167
168                key = unsafe { (*key).parent };
169            }
170
171            // When the top-level expression is `t[x]`, we want to refine it into `nil`, not `never`.
172            let prop_def = unsafe { (*prop_key).def } as DefId;
173            LUAU_ASSERT!(unsafe { (*refis).get(&prop_def) }.is_some());
174            unsafe {
175                (*refis).get_mut(&prop_def).unwrap().should_append_nil_type = (sense || !eq)
176                    && contains_subscripted_definition(prop_def)
177                    && !implicit_from_call;
178            }
179        }
180    }
181}