Skip to main content

luaur_analysis/methods/
constraint_generator_apply_refinements.rs

1use crate::enums::refinements_op_kind::RefinementsOpKind;
2use crate::enums::value::Value;
3use crate::functions::must_defer_intersection::must_defer_intersection;
4use crate::functions::should_suppress_errors_type_utils::should_suppress_errors;
5use crate::records::constraint_generator::ConstraintGenerator;
6use crate::records::constraint_generator::ConstraintGenerator as _ConstraintGenerator;
7use crate::records::error_suppression::ErrorSuppression;
8use crate::records::normalization_too_complex::NormalizationTooComplex;
9use crate::type_aliases::constraint_v::ConstraintV;
10use crate::type_aliases::refinement_context::RefinementContext;
11use crate::type_aliases::refinement_id_refinement::RefinementId;
12use crate::type_aliases::scope_ptr_constraint_generator::ScopePtr;
13use crate::type_aliases::type_error_data::TypeErrorData;
14use crate::type_aliases::type_id::TypeId;
15use alloc::vec::Vec;
16use luaur_ast::records::location::Location;
17use luaur_common::macros::luau_assert::LUAU_ASSERT;
18
19impl ConstraintGenerator {
20    pub fn apply_refinements(
21        &mut self,
22        scope: &ScopePtr,
23        location: Location,
24        refinement: RefinementId,
25    ) {
26        if refinement.is_null() {
27            return;
28        }
29
30        let mut refinements: RefinementContext = RefinementContext::default();
31        let mut constraints: Vec<ConstraintV> = Vec::new();
32
33        self.compute_refinement(
34            scope.as_ref() as *const _ as *mut _,
35            location,
36            refinement,
37            &mut refinements,
38            true,
39            false,
40            &mut constraints,
41        );
42
43        let mut flush_constraints = |this: &mut ConstraintGenerator,
44                                     kind: RefinementsOpKind,
45                                     ty: TypeId,
46                                     discriminants: &mut Vec<TypeId>|
47         -> TypeId {
48            if discriminants.is_empty() {
49                return ty;
50            }
51
52            if kind == RefinementsOpKind::None {
53                LUAU_ASSERT!(false);
54                return ty;
55            }
56
57            let mut args = Vec::new();
58            args.push(ty);
59
60            let builtin_types = this.builtin_types;
61            let type_functions = unsafe { &(*builtin_types).typeFunctions };
62
63            let func = if kind == RefinementsOpKind::Intersect {
64                &type_functions.intersect_func
65            } else {
66                &type_functions.refine_func
67            };
68
69            LUAU_ASSERT!(!func.name.is_empty());
70            args.extend_from_slice(discriminants.as_slice());
71
72            let result_type =
73                this.create_type_function_instance(func, args, Vec::new(), scope, location);
74            discriminants.clear();
75            result_type
76        };
77
78        let scope_raw = scope.as_ref() as *const _ as *mut _;
79
80        for (def, partition) in refinements.iter() {
81            let def_ty = self.lookup(scope, location, *def, false);
82            let Some(def_ty) = def_ty else { continue };
83
84            let mut ty = def_ty;
85
86            let mut discriminants: Vec<TypeId> = Vec::new();
87            let mut kind = RefinementsOpKind::None;
88
89            let mut must_defer = must_defer_intersection(ty);
90
91            for dt in &partition.discriminant_types {
92                let dt_val = *dt;
93
94                must_defer = must_defer || must_defer_intersection(dt_val);
95
96                if must_defer {
97                    if kind == RefinementsOpKind::Intersect {
98                        ty = flush_constraints(self, kind, ty, &mut discriminants);
99                    }
100                    kind = RefinementsOpKind::Refine;
101                    discriminants.push(dt_val);
102                } else {
103                    let status: ErrorSuppression = should_suppress_errors(self.normalizer, ty);
104
105                    if status.value == Value::NormalizationFailed {
106                        self.report_error(location, NormalizationTooComplex::default().into());
107                    }
108
109                    if kind == RefinementsOpKind::Refine {
110                        ty = flush_constraints(self, kind, ty, &mut discriminants);
111                    }
112                    kind = RefinementsOpKind::Intersect;
113
114                    discriminants.push(dt_val);
115
116                    if status.value == Value::Suppress {
117                        ty = flush_constraints(self, kind, ty, &mut discriminants);
118                        ty = self.make_union_scope_ptr_location_type_id_type_id(
119                            scope_raw,
120                            location,
121                            ty,
122                            unsafe { (*self.builtin_types).errorType },
123                        );
124                    }
125                }
126            }
127
128            if kind != RefinementsOpKind::None {
129                ty = flush_constraints(self, kind, ty, &mut discriminants);
130            }
131
132            if partition.should_append_nil_type {
133                ty = self.create_type_function_instance(
134                    unsafe { &(*self.builtin_types).typeFunctions.weakoptional_func },
135                    vec![ty],
136                    Vec::new(),
137                    scope,
138                    location,
139                );
140            }
141
142            self.update_r_value_refinements_scope_ptr_def_id_type_id(scope, *def, ty);
143        }
144
145        for c in constraints {
146            self.add_constraint_scope_ptr_location_constraint_v(scope, location, c);
147        }
148    }
149}