Skip to content

refactor generic#1081

Draft
xuhuanzy wants to merge 6 commits into
EmmyLuaLs:mainfrom
xuhuanzy:refactor-generic
Draft

refactor generic#1081
xuhuanzy wants to merge 6 commits into
EmmyLuaLs:mainfrom
xuhuanzy:refactor-generic

Conversation

@xuhuanzy
Copy link
Copy Markdown
Member

No description provided.

@xuhuanzy xuhuanzy changed the title refactor(generic): Add more precise type widening refactor generic May 14, 2026
@github-actions

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as resolved.

@xuhuanzy
Copy link
Copy Markdown
Member Author

/gemini review

gemini-code-assist[bot]

This comment was marked as resolved.

@xuhuanzy
Copy link
Copy Markdown
Member Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive overhaul of the generic inference system, transitioning to a constraint-based model that supports co- and contravariant variance and improves the handling of structural literals. Key additions include the implementation of Pick and Omit utility types, a pre-analysis phase for nested table fields in function arguments, and a refactored type instantiation logic to better manage recursive aliases. Review feedback identifies several necessary refinements to the new inference engine: resolve_unpack_operands should be updated to fully expand variadic parameters, the __pairs metamethod logic needs to correctly unpack the return tuple to isolate the iterator function, and support for MultiLineUnion and ConstTplRef should be added to is_literal_candidate and variadic inference branches, respectively, to ensure type consistency.

Comment on lines +269 to +276
SubstitutorValue::Params(params) => Some(
params
.first()
.unwrap_or(&(String::new(), None))
.1
.clone()
.unwrap_or(LuaType::Unknown),
),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation for SubstitutorValue::Params in resolve_unpack_operands only takes the first parameter from the variadic list, which causes information loss when unpacking a variadic template. It should expand all parameters into a VariadicType::Multi to correctly support multi-return values from variadic generics.

Suggested change
SubstitutorValue::Params(params) => Some(
params
.first()
.unwrap_or(&(String::new(), None))
.1
.clone()
.unwrap_or(LuaType::Unknown),
),
SubstitutorValue::Params(params) => Some(LuaType::Variadic(
VariadicType::Multi(
params
.iter()
.map(|(_, ty)| ty.clone().unwrap_or(LuaType::Unknown))
.collect(),
)
.into(),
)),

Comment on lines +1882 to +1890
let final_return_type = match meta_return {
LuaType::DocFunction(doc_func) => Some(doc_func.get_ret().clone()),
LuaType::Signature(signature_id) => context
.db
.get_signature_index()
.get(&signature_id)
.map(|signature| signature.get_return_type()),
_ => None,
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for handling __pairs metamethod return types is incomplete. __pairs typically returns a tuple (iterator, state, initial_value). The current code tries to match the entire return tuple against DocFunction or Signature, which will fail if it's a multi-return. It should first extract the iterator function (the first slot of the return) before attempting to get its return type.

    let iterator_func = meta_return.get_result_slot_type(0).unwrap_or(meta_return);
    let final_return_type = match iterator_func {
        LuaType::DocFunction(doc_func) => Some(doc_func.get_ret().clone()),
        LuaType::Signature(signature_id) => context.db.get_signature_index()
            .get(&signature_id)
            .map(|signature| signature.get_return_type()),
        _ => None,
    };

Comment on lines +2090 to +2108
pub(in crate::semantic::generic) fn is_literal_candidate(ty: &LuaType) -> bool {
match ty {
LuaType::StringConst(_)
| LuaType::DocStringConst(_)
| LuaType::IntegerConst(_)
| LuaType::DocIntegerConst(_)
| LuaType::FloatConst(_)
| LuaType::BooleanConst(_)
| LuaType::DocBooleanConst(_)
| LuaType::TableConst(_) => true,
LuaType::Union(union) => union.into_vec().iter().any(is_literal_candidate),
LuaType::Tuple(tuple) => tuple.get_types().iter().any(is_literal_candidate),
LuaType::Variadic(variadic) => match variadic.deref() {
VariadicType::Base(base) => is_literal_candidate(base),
VariadicType::Multi(types) => types.iter().any(is_literal_candidate),
},
_ => false,
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

is_literal_candidate is missing handling for LuaType::MultiLineUnion. This might cause literal preservation logic to fail for templates inferred from multi-line unions.

pub(in crate::semantic::generic) fn is_literal_candidate(ty: &LuaType) -> bool {
    match ty {
        LuaType::StringConst(_)
        | LuaType::DocStringConst(_)
        | LuaType::IntegerConst(_)
        | LuaType::DocIntegerConst(_)
        | LuaType::FloatConst(_)
        | LuaType::BooleanConst(_)
        | LuaType::DocBooleanConst(_)
        | LuaType::TableConst(_) => true,
        LuaType::Union(union) => union.into_vec().iter().any(is_literal_candidate),
        LuaType::MultiLineUnion(multi) => multi
            .get_unions()
            .iter()
            .any(|(ty, _)| is_literal_candidate(ty)),
        LuaType::Tuple(tuple) => tuple.get_types().iter().any(is_literal_candidate),
        LuaType::Variadic(variadic) => match variadic.deref() {
            VariadicType::Base(base) => is_literal_candidate(base),
            VariadicType::Multi(types) => types.iter().any(is_literal_candidate),
        },
        _ => false,
    }
}

Comment on lines +1313 to +1321
if let LuaType::TplRef(tpl_ref) = source_base {
context.insert_type(
tpl_ref.get_tpl_id(),
InferenceCandidate::ordinary(target_base.clone()),
variance,
false,
priority,
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This variadic inference branch only handles TplRef, but it should also support ConstTplRef to ensure that constant templates are correctly inferred when used as variadic bases. Similar omissions exist in other variadic inference helpers in this file (e.g., function_varargs_infer_types and tuple_infer_types).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant