-
Notifications
You must be signed in to change notification settings - Fork 22
Description
Summary
When a function object variable is a concrete instantiation of a class template, the lifted operator() signature still contains unresolved template parameters. For example:
template<typename T>
struct compare_fn {
bool operator()(T const& a, T const& b) const;
};
constexpr compare_fn<int> compare_int = {};The generated documentation for compare_int shows operator()(T const& a, T const& b) instead of operator()(int const& a, int const& b).
This does not affect variable templates (where the parameter is intentionally unresolved) or non-template structs with template operator(), both of which already work correctly.
Proposed approach
The template arguments are already available in the metadata. The variable's type carries a SpecializationName with TemplateArgs, and the record's TemplateInfo has the corresponding Params. The substitution could happen in FunctionObjectFinalizer::processVariable(), after the synthetic function is created from the original operator():
- Build a mapping from template parameter names to concrete types by pairing
record.Template.Params[i]withspecName.TemplateArgs[i]. - Recursively walk the synthetic function's return type and parameter types.
- Replace any
NamedTypethat matches a template parameter name with the concrete type.
Open questions
Feedback welcome on any of these:
- Substitution depth. The recursive walk needs to handle all
TypeKindvariants: pointers, references, arrays, function types, member pointers, and nested specializations (e.g.,std::vector<T>→std::vector<int>). Are there type kinds where substitution should be skipped or handled specially? - Name matching. Template parameter names like
Tare stored as string identifiers inName. Is string matching againstrecord.Template.Params[i].Namesufficient, or are there cases where the same name could appear at different template depths and cause ambiguity? - Non-type and template-template parameters. The initial implementation could focus on type parameters only. Are there realistic function object patterns that use non-type template parameters (e.g.,
template<int N>) or template-template parameters inoperator()signatures? - Finalization vs. extraction. Should the substitution happen at finalization time (in
FunctionObjectFinalizer) or earlier during AST extraction? Finalization keeps the change localized, but extraction has access to Clang'sSubstTemplateTypeParmTypewhich might already carry the resolved types. - Partial specializations. If
compare_fn<int>is a partial specialization with a differentoperator()signature, does the current finalizer already pick up the specialization's members, or does it always look at the primary template?
Context
This came up during the function object support work (PR #1157). The compare_int / compare_double test cases in test-files/golden-tests/symbols/variable/function-objects.cpp show the current behavior.