Skip to main content

luaur_analysis/functions/
are_compatible.rs

1use crate::functions::follow_type::follow_type_id;
2use crate::functions::get_2::get2;
3use crate::functions::is_optional_or_free::is_optional_or_free;
4use crate::records::property_type::Property;
5use crate::records::table_type::TableType;
6use crate::type_aliases::type_id::TypeId;
7use luaur_common::macros::luau_assert::LUAU_ASSERT;
8
9// Two tables may be compatible even if their shapes aren't exactly the
10// same if the extra property is optional, free (and therefore
11// potentially optional), or if the right table has an indexer.  Or if
12// the right table is free (and therefore potentially has an indexer or
13// a compatible property)
14unsafe fn missing_prop_is_compatible(left_prop: &Property, right_table: &TableType) -> bool {
15    if right_table.state == crate::enums::table_state::TableState::Free
16        || right_table.indexer.is_some()
17    {
18        return true;
19    }
20
21    if left_prop.is_read_only() || left_prop.is_shared() {
22        if is_optional_or_free(left_prop.read_ty.unwrap()) {
23            return true;
24        }
25    }
26
27    // FIXME: Could this create an issue for write only / divergent properties?
28    false
29}
30
31pub unsafe fn are_compatible(left: TypeId, right: TypeId) -> bool {
32    let p = get2::<TableType, TableType, TypeId>(follow_type_id(left), follow_type_id(right));
33    if p.first.is_null() {
34        return true;
35    }
36
37    let left_table = p.first;
38    LUAU_ASSERT!(!left_table.is_null());
39    let right_table = p.second;
40    LUAU_ASSERT!(!right_table.is_null());
41
42    let left_table = &*left_table;
43    let right_table = &*right_table;
44
45    for (_name, left_prop) in left_table.props.iter() {
46        let it = right_table.props.get(_name);
47        if it.is_none() {
48            if !missing_prop_is_compatible(left_prop, right_table) {
49                return false;
50            }
51        }
52    }
53
54    for (_name, right_prop) in right_table.props.iter() {
55        let it = left_table.props.get(_name);
56        if it.is_none() {
57            if !missing_prop_is_compatible(right_prop, left_table) {
58                return false;
59            }
60        }
61    }
62
63    true
64}