Skip to main content

luaur_analysis/functions/
reduce_functions_internal.rs

1//! `reduceFunctionsInternal` (TypeFunction.cpp:657-693).
2
3use crate::records::code_too_complex::CodeTooComplex;
4use crate::records::function_graph_reduction_result::FunctionGraphReductionResult;
5use crate::records::type_error::TypeError;
6use crate::records::type_function_context::TypeFunctionContext;
7use crate::records::type_function_reducer::TypeFunctionReducer;
8use crate::records::type_reduction_reentrancy_guard::TypeReductionReentrancyGuard;
9use crate::type_aliases::type_error_data::TypeErrorData;
10use crate::type_aliases::type_id::TypeId;
11use crate::type_aliases::type_or_type_pack_id_set::TypeOrTypePackIdSet;
12use crate::type_aliases::type_pack_id::TypePackId;
13use alloc::vec::Vec;
14use core::ptr::NonNull;
15use luaur_ast::records::location::Location;
16use luaur_common::records::vec_deque::VecDeque;
17
18pub fn reduce_functions_internal(
19    queued_tys: VecDeque<TypeId>,
20    queued_tps: VecDeque<TypePackId>,
21    should_guess: TypeOrTypePackIdSet,
22    cyclics: Vec<TypeId>,
23    location: Location,
24    ctx: NonNull<TypeFunctionContext>,
25    force: bool,
26) -> FunctionGraphReductionResult {
27    let mut reducer = TypeFunctionReducer::type_function_reducer(
28        queued_tys,
29        queued_tps,
30        should_guess,
31        cyclics,
32        location,
33        ctx,
34        force,
35    );
36    let mut iteration_count: i32 = 0;
37
38    // If we are reducing a type function while reducing a type function,
39    // we're probably doing something clowny. One known place this can
40    // occur is type function reduction => overload selection => subtyping
41    // => back to type function reduction. At worst, if there's a reduction
42    // that _doesn't_ loop forever and _needs_ reentrancy, we'll fail to
43    // handle that and potentially emit an error when we didn't need to.
44    let shared_state = unsafe { (*(*ctx.as_ptr()).normalizer.as_ptr()).shared_state };
45    if unsafe { !shared_state.is_null() && (*shared_state).reentrant_type_reduction } {
46        return FunctionGraphReductionResult {
47            errors: alloc::vec::Vec::new(),
48            messages: alloc::vec::Vec::new(),
49            blocked_types: luaur_common::records::dense_hash_set::DenseHashSet::new(
50                core::ptr::null(),
51            ),
52            blocked_packs: luaur_common::records::dense_hash_set::DenseHashSet::new(
53                core::ptr::null(),
54            ),
55            reduced_types: luaur_common::records::dense_hash_set::DenseHashSet::new(
56                core::ptr::null(),
57            ),
58            reduced_packs: luaur_common::records::dense_hash_set::DenseHashSet::new(
59                core::ptr::null(),
60            ),
61            irreducible_types: luaur_common::records::dense_hash_set::DenseHashSet::new(
62                core::ptr::null(),
63            ),
64        };
65    }
66
67    // TypeReductionReentrancyGuard _{ctx->normalizer->sharedState};
68    // RAII: sets reentrant_type_reduction = true now, resets to false on scope exit.
69    let _guard =
70        TypeReductionReentrancyGuard::type_reduction_reentrancy_guard_not_null_unifier_shared_state(
71            shared_state,
72        );
73
74    let max_steps = luaur_common::DFInt::LuauTypeFamilyGraphReductionMaximumSteps.get();
75
76    while !reducer.done() {
77        reducer.step();
78
79        iteration_count += 1;
80        if iteration_count > max_steps {
81            reducer
82                .result
83                .errors
84                .push(TypeError::type_error_location_type_error_data(
85                    location,
86                    TypeErrorData::CodeTooComplex(CodeTooComplex::default()),
87                ));
88            break;
89        }
90    }
91
92    // The Rust `TypeReductionReentrancyGuard` has no `Drop` impl yet, so mirror
93    // the C++ destructor (`sharedState->reentrantTypeReduction = false`) here at
94    // scope exit to preserve the RAII semantics faithfully.
95    unsafe {
96        if !shared_state.is_null() {
97            (*shared_state).reentrant_type_reduction = false;
98        }
99    }
100    drop(_guard);
101
102    core::mem::replace(
103        &mut reducer.result,
104        FunctionGraphReductionResult {
105            errors: alloc::vec::Vec::new(),
106            messages: alloc::vec::Vec::new(),
107            blocked_types: luaur_common::records::dense_hash_set::DenseHashSet::new(
108                core::ptr::null(),
109            ),
110            blocked_packs: luaur_common::records::dense_hash_set::DenseHashSet::new(
111                core::ptr::null(),
112            ),
113            reduced_types: luaur_common::records::dense_hash_set::DenseHashSet::new(
114                core::ptr::null(),
115            ),
116            reduced_packs: luaur_common::records::dense_hash_set::DenseHashSet::new(
117                core::ptr::null(),
118            ),
119            irreducible_types: luaur_common::records::dense_hash_set::DenseHashSet::new(
120                core::ptr::null(),
121            ),
122        },
123    )
124}