Skip to main content

luaur_analysis/methods/
subtyping_is_covariant_with_deprecated.rs

1use crate::enums::subtyping_suppression_policy::SubtypingSuppressionPolicy;
2use crate::enums::table_state::TableState;
3use crate::enums::type_field::TypeField;
4use crate::records::property_type::Property;
5use crate::records::property_type_path::Property as PathProperty;
6use crate::records::scope::Scope;
7use crate::records::subtyping::Subtyping;
8use crate::records::subtyping_environment::SubtypingEnvironment;
9use crate::records::subtyping_result::SubtypingResult;
10use crate::records::table_type::TableType;
11use crate::type_aliases::component::Component;
12use alloc::string::ToString;
13use alloc::vec::Vec;
14use luaur_common::FFlag;
15
16fn path_property(name: &str, is_read: bool) -> Component {
17    Component::Property(PathProperty {
18        name: name.to_string(),
19        is_read,
20    })
21}
22
23fn index_result_component() -> Component {
24    Component::TypeField(TypeField::IndexResult)
25}
26
27impl Subtyping {
28    pub fn is_covariant_with_deprecated(
29        &mut self,
30        env: &mut SubtypingEnvironment,
31        sub_table: &TableType,
32        super_table: &TableType,
33        force_covariant_test: bool,
34        scope: *mut Scope,
35    ) -> SubtypingResult {
36        let mut result = SubtypingResult {
37            is_subtype: true,
38            ..Default::default()
39        };
40
41        if sub_table.props.is_empty()
42            && sub_table.indexer.is_none()
43            && sub_table.state == TableState::Sealed
44            && super_table.indexer.is_some()
45        {
46            return SubtypingResult {
47                is_subtype: false,
48                ..Default::default()
49            };
50        }
51
52        for (name, super_prop) in &super_table.props {
53            let mut results = Vec::new();
54
55            if let Some(sub_prop) = sub_table.props.get(name) {
56                results.push(
57                    self.is_covariant_with_subtyping_environment_property_property_string_bool_not_null_scope(
58                        env,
59                        sub_prop,
60                        super_prop,
61                        name,
62                        force_covariant_test,
63                        scope,
64                    ),
65                );
66            } else if let Some(sub_indexer) = &sub_table.indexer {
67                let can_index_by_string = unsafe {
68                    self.is_covariant_with_subtyping_environment_type_id_type_id_not_null_scope(
69                        env,
70                        (*self.builtin_types).stringType,
71                        sub_indexer.index_type,
72                        scope,
73                    )
74                    .is_subtype
75                };
76
77                if can_index_by_string {
78                    if super_prop.is_shared() {
79                        if FFlag::LuauReadOnlyIndexers.get() && sub_indexer.is_read_only {
80                            let mut sr = SubtypingResult {
81                                is_subtype: false,
82                                ..Default::default()
83                            };
84                            sr.with_sub_component(index_result_component());
85                            sr.with_super_component(path_property(name, true));
86                            results.push(sr);
87                        } else {
88                            let mut sr = self
89                                .is_invariant_with_subtyping_environment_sub_ty_super_ty_not_null_scope(
90                                    env,
91                                    sub_indexer.index_result_type,
92                                    super_prop.read_ty.unwrap(),
93                                    scope,
94                                );
95                            sr.with_sub_component(index_result_component());
96                            sr.with_super_component(path_property(name, true));
97                            results.push(sr);
98                        }
99                    } else {
100                        if let Some(super_read_ty) = super_prop.read_ty {
101                            let mut sr = self
102                                .is_covariant_with_subtyping_environment_type_id_type_id_not_null_scope(
103                                    env,
104                                    sub_indexer.index_result_type,
105                                    super_read_ty,
106                                    scope,
107                                );
108                            sr.with_sub_component(index_result_component());
109                            sr.with_super_component(path_property(name, true));
110                            results.push(sr);
111                        }
112
113                        if let Some(super_write_ty) = super_prop.write_ty {
114                            if FFlag::LuauReadOnlyIndexers.get() && sub_indexer.is_read_only {
115                                let mut sr = SubtypingResult {
116                                    is_subtype: false,
117                                    ..Default::default()
118                                };
119                                sr.with_sub_component(index_result_component());
120                                sr.with_super_component(path_property(name, false));
121                                results.push(sr);
122                            } else {
123                                let mut sr = self
124                                    .is_contravariant_with_subtyping_environment_sub_ty_super_ty_not_null_scope(
125                                        env,
126                                        sub_indexer.index_result_type,
127                                        super_write_ty,
128                                        scope,
129                                    );
130                                sr.with_sub_component(index_result_component());
131                                sr.with_super_component(path_property(name, false));
132                                results.push(sr);
133                            }
134                        }
135                    }
136                }
137            } else if FFlag::LuauSubtypingMissingPropertiesAsNil.get() {
138                let nil_prop = unsafe { Property::readonly((*self.builtin_types).nilType) };
139                let mut sr =
140                    self.is_covariant_with_subtyping_environment_property_property_string_bool_not_null_scope(
141                        env,
142                        &nil_prop,
143                        super_prop,
144                        name,
145                        force_covariant_test,
146                        scope,
147                    );
148                sr.reasoning.clear();
149                results.push(sr);
150            }
151
152            if results.is_empty() {
153                return SubtypingResult {
154                    is_subtype: false,
155                    ..Default::default()
156                };
157            }
158
159            let is_subtype = results.iter().all(|sr| sr.is_subtype);
160
161            if result.is_subtype && !is_subtype {
162                for sr in results {
163                    result.and_also(sr, SubtypingSuppressionPolicy::Any);
164                }
165            } else {
166                for sr in results {
167                    result.and_also(sr, SubtypingSuppressionPolicy::All);
168                }
169            }
170        }
171
172        if let Some(super_indexer) = &super_table.indexer {
173            if let Some(sub_indexer) = &sub_table.indexer {
174                let indexer_result = if FFlag::LuauReadOnlyIndexers.get() {
175                    self.is_covariant_with_subtyping_environment_table_indexer_table_indexer_not_null_scope(
176                        env,
177                        sub_indexer,
178                        super_indexer,
179                        scope,
180                    )
181                } else {
182                    self.is_invariant_with_subtyping_environment_sub_ty_super_ty_not_null_scope(
183                        env,
184                        *sub_indexer,
185                        *super_indexer,
186                        scope,
187                    )
188                };
189                result.and_also(indexer_result, SubtypingSuppressionPolicy::All);
190            } else if sub_table.state != TableState::Sealed {
191                return SubtypingResult {
192                    is_subtype: true,
193                    ..Default::default()
194                };
195            } else {
196                return SubtypingResult {
197                    is_subtype: false,
198                    ..Default::default()
199                };
200            }
201        }
202
203        result
204    }
205}