Skip to main content

luaur_compiler/functions/
get_type.rs

1use luaur_ast::records::ast_array::AstArray;
2use luaur_ast::records::ast_generic_type::AstGenericType;
3use luaur_ast::records::ast_name::AstName;
4use luaur_ast::records::ast_stat_type_alias::AstStatTypeAlias;
5use luaur_ast::records::ast_type::AstType;
6use luaur_ast::records::ast_type_function::AstTypeFunction;
7use luaur_ast::records::ast_type_group::AstTypeGroup;
8use luaur_ast::records::ast_type_intersection::AstTypeIntersection;
9use luaur_ast::records::ast_type_optional::AstTypeOptional;
10use luaur_ast::records::ast_type_reference::AstTypeReference;
11use luaur_ast::records::ast_type_singleton_bool::AstTypeSingletonBool;
12use luaur_ast::records::ast_type_singleton_string::AstTypeSingletonString;
13use luaur_ast::records::ast_type_table::AstTypeTable;
14use luaur_ast::records::ast_type_union::AstTypeUnion;
15use luaur_ast::rtti::{ast_node_as, ast_node_is};
16use luaur_bytecode::records::bytecode_builder::BytecodeBuilder;
17use luaur_common::enums::luau_bytecode_type::{
18    LuauBytecodeType, LBC_TYPE_ANY, LBC_TYPE_BOOLEAN, LBC_TYPE_FUNCTION, LBC_TYPE_INVALID,
19    LBC_TYPE_NIL, LBC_TYPE_NUMBER, LBC_TYPE_OPTIONAL_BIT, LBC_TYPE_STRING, LBC_TYPE_TABLE,
20    LBC_TYPE_TAGGED_USERDATA_BASE, LBC_TYPE_USERDATA, LBC_TYPE_VECTOR,
21};
22use luaur_common::records::dense_hash_map::DenseHashMap;
23use luaur_common::records::dense_hash_set::DenseHashSet;
24use luaur_common::FFlag;
25
26pub fn get_type(
27    ty: *const AstType,
28    generics: AstArray<*mut AstGenericType>,
29    type_aliases: &DenseHashMap<AstName, *mut AstStatTypeAlias>,
30    resolve_aliases_deprecated: bool,
31    host_vector_type: *const core::ffi::c_char,
32    userdata_types: &DenseHashMap<AstName, u8>,
33    bytecode: &mut BytecodeBuilder,
34    seen_aliases: &mut DenseHashSet<AstName>,
35) -> LuauBytecodeType {
36    if ty.is_null() {
37        return LBC_TYPE_ANY;
38    }
39
40    let node = ty as *mut luaur_ast::records::ast_node::AstNode;
41
42    if let Some(ref_node) = unsafe { ast_node_as::<AstTypeReference>(node).as_ref() } {
43        if ref_node.prefix.is_some() {
44            return LBC_TYPE_ANY;
45        }
46
47        // C++ `if (alias && *alias)` — the entry may exist with a NULL value (a
48        // block-scoped alias restored to its previous null binding on scope exit), in
49        // which case it is NOT in scope and we must fall through to the generic/userdata
50        // resolution. Only resolve when the stored pointer is non-null.
51        if let Some(alias_ptr) = type_aliases
52            .find(&ref_node.name)
53            .copied()
54            .filter(|p| !p.is_null())
55        {
56            let alias = unsafe { &*alias_ptr };
57            if FFlag::LuauCompileTypeAliases.get() {
58                if seen_aliases.contains(&alias.name) {
59                    seen_aliases.clear();
60                    return LBC_TYPE_ANY;
61                } else {
62                    seen_aliases.insert(ref_node.name);
63                    return get_type(
64                        alias.type_ptr,
65                        alias.generics,
66                        type_aliases,
67                        /* resolveAliases_DEPRECATED= */ false,
68                        host_vector_type,
69                        userdata_types,
70                        bytecode,
71                        seen_aliases,
72                    );
73                }
74            } else {
75                // note: we only resolve aliases to the depth of 1 to avoid dealing with recursive aliases
76                if resolve_aliases_deprecated {
77                    return get_type(
78                        alias.type_ptr,
79                        alias.generics,
80                        type_aliases,
81                        /* resolveAliases_DEPRECATED= */ false,
82                        host_vector_type,
83                        userdata_types,
84                        bytecode,
85                        seen_aliases,
86                    );
87                } else {
88                    return LBC_TYPE_ANY;
89                }
90            }
91        }
92
93        if crate::functions::is_generic::is_generic(ref_node.name, &generics) {
94            return LBC_TYPE_ANY;
95        }
96
97        if !host_vector_type.is_null() && ref_node.name.operator_eq_c_char(host_vector_type) {
98            return LBC_TYPE_VECTOR;
99        }
100
101        let prim = crate::functions::get_primitive_type::get_primitive_type(ref_node.name);
102        if prim != LBC_TYPE_INVALID {
103            return prim;
104        }
105
106        if let Some(userdata_index) = userdata_types.find(&ref_node.name) {
107            bytecode.use_userdata_type(*userdata_index as u32);
108            return LuauBytecodeType(LBC_TYPE_TAGGED_USERDATA_BASE.0 + *userdata_index as u16);
109        }
110
111        // not primitive or alias or generic => host-provided, we assume userdata for now
112        return LBC_TYPE_USERDATA;
113    } else if unsafe { ast_node_is::<AstTypeTable>(node) } {
114        return LBC_TYPE_TABLE;
115    } else if unsafe { ast_node_is::<AstTypeFunction>(node) } {
116        return LBC_TYPE_FUNCTION;
117    } else if let Some(un) = unsafe { ast_node_as::<AstTypeUnion>(node).as_ref() } {
118        let mut optional = false;
119        let mut r#type = LBC_TYPE_INVALID;
120
121        for &ty in un.types.as_slice() {
122            let et = get_type(
123                ty,
124                generics,
125                type_aliases,
126                resolve_aliases_deprecated,
127                host_vector_type,
128                userdata_types,
129                bytecode,
130                seen_aliases,
131            );
132
133            if et == LBC_TYPE_NIL {
134                optional = true;
135                continue;
136            }
137
138            if r#type == LBC_TYPE_INVALID {
139                r#type = et;
140                continue;
141            }
142
143            if r#type != et {
144                return LBC_TYPE_ANY;
145            }
146        }
147
148        if r#type == LBC_TYPE_INVALID {
149            return LBC_TYPE_ANY;
150        }
151
152        return LuauBytecodeType(
153            r#type.0
154                | (if optional && (r#type != LBC_TYPE_ANY) {
155                    LBC_TYPE_OPTIONAL_BIT.0
156                } else {
157                    0
158                }),
159        );
160    } else if unsafe { ast_node_is::<AstTypeIntersection>(node) } {
161        return LBC_TYPE_ANY;
162    } else if let Some(group) = unsafe { ast_node_as::<AstTypeGroup>(node).as_ref() } {
163        return get_type(
164            group.type_,
165            generics,
166            type_aliases,
167            resolve_aliases_deprecated,
168            host_vector_type,
169            userdata_types,
170            bytecode,
171            seen_aliases,
172        );
173    } else if unsafe { ast_node_is::<AstTypeOptional>(node) } {
174        return LBC_TYPE_NIL;
175    } else if unsafe { ast_node_is::<AstTypeSingletonBool>(node) } {
176        return LBC_TYPE_BOOLEAN; // C++ returns LBC_TYPE_BOOLEAN for `true`/`false`
177    } else if unsafe { ast_node_is::<AstTypeSingletonString>(node) } {
178        return LBC_TYPE_STRING;
179    }
180
181    LBC_TYPE_ANY
182}