Skip to main content

luaur_analysis/methods/
unifier_try_unify_scalar_shape.rs

1//! Source: `Analysis/src/Unifier.cpp` (Unifier::tryUnifyScalarShape, L2151-2224)
2use crate::enums::normalization_result::NormalizationResult;
3use crate::enums::table_state::TableState;
4use crate::functions::get_metatable_type::get_metatable_type_id_not_null_builtin_types;
5use crate::functions::has_unification_too_complex::has_unification_too_complex;
6use crate::records::r#type::Type;
7use crate::records::table_type::TableType;
8use crate::records::type_error::TypeError;
9use crate::records::type_mismatch::TypeMismatch;
10use crate::records::unifier::Unifier;
11use crate::type_aliases::type_error_data::TypeErrorData;
12use crate::type_aliases::type_id::TypeId;
13use crate::type_aliases::type_variant::TypeVariant;
14use alloc::string::String;
15use alloc::sync::Arc;
16
17impl Unifier {
18    /// `void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed)`
19    pub fn unifier_try_unify_scalar_shape(
20        &mut self,
21        mut sub_ty: TypeId,
22        mut super_ty: TypeId,
23        reversed: bool,
24    ) {
25        let osub_ty = sub_ty;
26        let osuper_ty = super_ty;
27
28        // If the normalizer hits resource limits, we can't show it's uninhabited, so, we should continue.
29        if self.check_inhabited
30            && unsafe { (*self.normalizer).is_inhabited_type_id(sub_ty) }
31                == NormalizationResult::False
32        {
33            return;
34        }
35
36        if reversed {
37            core::mem::swap(&mut sub_ty, &mut super_ty);
38        }
39
40        let super_table = self.log.txn_log_get_mutable::<TableType, TypeId>(super_ty);
41
42        if super_table.is_null() || unsafe { (*super_table).state } != TableState::Free {
43            let context = self.unifier_mismatch_context();
44            self.report_error_location_type_error_data(
45                self.location,
46                TypeErrorData::TypeMismatch(TypeMismatch {
47                    wanted_type: osuper_ty,
48                    given_type: osub_ty,
49                    reason: String::new(),
50                    error: None,
51                    context,
52                }),
53            );
54            return;
55        }
56
57        // Given t1 where t1 = { lower: (t1) -> (a, b...) }
58        // It should be the case that `string <: t1` iff `(subtype's metatable).__index <: t1`
59        if let Some(metatable) =
60            get_metatable_type_id_not_null_builtin_types(sub_ty, unsafe { &*self.builtin_types })
61        {
62            let mttv = self.log.txn_log_get::<TableType, TypeId>(metatable);
63            if mttv.is_null() {
64                self.scalar_shape_fail(osuper_ty, osub_ty, None);
65            }
66
67            if let Some(prop) = unsafe { (*mttv).props.get("__index") } {
68                let ty = prop.type_deprecated();
69                let mut child = self.unifier_make_child_unifier();
70                child.try_unify_type_id_type_id_bool_bool_literal_properties(
71                    ty, super_ty, false, false, None,
72                );
73
74                // To perform subtype <: free table unification, we have tried to unify (subtype's metatable) <: free table
75                // There is a chance that it was unified with the original subtype, but then, (subtype's metatable) <: subtype could've failed
76                // Here we check if we have a new supertype instead of the original free table and try original subtype <: new supertype check
77                let new_super_ty = child.log.follow_type_id(super_ty);
78
79                if super_ty != new_super_ty
80                    && self
81                        .can_unify_type_id_type_id(sub_ty, new_super_ty)
82                        .is_empty()
83                {
84                    self.log
85                        .replace_type_id_t(super_ty, Type::new(TypeVariant::Bound(sub_ty)));
86                    return;
87                }
88
89                if let Some(e) = has_unification_too_complex(&child.errors) {
90                    self.report_error_type_error(e);
91                } else if !child.errors.is_empty() {
92                    let first = child.errors[0].clone();
93                    self.scalar_shape_fail(osuper_ty, osub_ty, Some(first));
94                }
95
96                let child_errors_empty = child.errors.is_empty();
97                self.log.concat(child.log);
98
99                // To perform subtype <: free table unification, we have tried to unify (subtype's metatable) <: free table
100                // We return success because subtype <: free table which means that correct unification is to replace free table with the subtype
101                if child_errors_empty {
102                    self.log
103                        .replace_type_id_t(super_ty, Type::new(TypeVariant::Bound(sub_ty)));
104                }
105
106                return;
107            } else {
108                self.scalar_shape_fail(osuper_ty, osub_ty, None);
109                return;
110            }
111        }
112
113        let context = self.unifier_mismatch_context();
114        self.report_error_location_type_error_data(
115            self.location,
116            TypeErrorData::TypeMismatch(TypeMismatch {
117                wanted_type: osuper_ty,
118                given_type: osub_ty,
119                reason: String::new(),
120                error: None,
121                context,
122            }),
123        );
124    }
125
126    /// The `fail` lambda from `tryUnifyScalarShape`.
127    fn scalar_shape_fail(&mut self, osuper_ty: TypeId, osub_ty: TypeId, e: Option<TypeError>) {
128        let reason = String::from("The given type's metatable does not satisfy the requirements.");
129        let context = self.unifier_mismatch_context();
130        self.report_error_location_type_error_data(
131            self.location,
132            TypeErrorData::TypeMismatch(TypeMismatch {
133                wanted_type: osuper_ty,
134                given_type: osub_ty,
135                reason,
136                error: e.map(Arc::new),
137                context,
138            }),
139        );
140    }
141}