[clang] [clang-tools-extra] [Clang] Add builtins that deduplicate and sort types (PR #106730)
Ilya Biryukov via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 23 07:51:30 PDT 2025
================
@@ -5054,95 +5089,128 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
}
if (In.getArgument().isPackExpansion()) {
- // We have a pack expansion, for which we will be substituting into
- // the pattern.
- SourceLocation Ellipsis;
- UnsignedOrNone OrigNumExpansions = std::nullopt;
- TemplateArgumentLoc Pattern
- = getSema().getTemplateArgumentPackExpansionPattern(
- In, Ellipsis, OrigNumExpansions);
-
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
- assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
-
- // Determine whether the set of unexpanded parameter packs can and should
- // be expanded.
- bool Expand = true;
- bool RetainExpansion = false;
- UnsignedOrNone NumExpansions = OrigNumExpansions;
- if (getDerived().TryExpandParameterPacks(Ellipsis,
- Pattern.getSourceRange(),
- Unexpanded,
- Expand,
- RetainExpansion,
- NumExpansions))
+ if (ExpandTemplateArgument(In, Outputs, Uneval, true))
return true;
+ continue;
+ }
- if (!Expand) {
- // The transform has determined that we should perform a simple
- // transformation on the pack expansion, producing another pack
- // expansion.
- TemplateArgumentLoc OutPattern;
- Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), std::nullopt);
- if (getDerived().TransformTemplateArgument(Pattern, OutPattern, Uneval))
- return true;
+ // The simple case:
+ if (getDerived().TransformTemplateArgument(In, Out, Uneval))
+ return true;
- Out = getDerived().RebuildPackExpansion(OutPattern, Ellipsis,
- NumExpansions);
- if (Out.getArgument().isNull())
- return true;
+ Outputs.addArgument(Out);
+ }
- Outputs.addArgument(Out);
- continue;
- }
+ return false;
+}
- // The transform has determined that we should perform an elementwise
- // expansion of the pattern. Do so.
- for (unsigned I = 0; I != *NumExpansions; ++I) {
- Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), I);
+template <typename Derived>
+bool TreeTransform<Derived>::ExpandTemplateArgument(
+ TemplateArgumentLoc In, TemplateArgumentListInfo &Outputs, bool Uneval,
+ bool TryExpandingSubstPacks) {
+ assert(In.getArgument().isPackExpansion());
+ // We have a pack expansion, for which we will be substituting into the
+ // pattern.
+ SourceLocation Ellipsis;
+ UnsignedOrNone OrigNumExpansions = std::nullopt;
+ TemplateArgumentLoc Pattern =
+ getSema().getTemplateArgumentPackExpansionPattern(In, Ellipsis,
+ OrigNumExpansions);
- if (getDerived().TransformTemplateArgument(Pattern, Out, Uneval))
- return true;
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ if (!TryExpandingSubstPacks) {
+ // Only do something if there is an opportunity for late expansion.
+ bool SawPackTypes = llvm::any_of(Unexpanded, [](UnexpandedParameterPack P) {
+ return P.first.dyn_cast<const SubstBuiltinTemplatePackType *>();
+ });
+ if (!SawPackTypes) {
+ Outputs.addArgument(In);
+ return false;
+ }
+ }
+ assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
- if (Out.getArgument().containsUnexpandedParameterPack()) {
- Out = getDerived().RebuildPackExpansion(Out, Ellipsis,
- OrigNumExpansions);
- if (Out.getArgument().isNull())
- return true;
- }
+ // Determine whether the set of unexpanded parameter packs can and should
+ // be expanded.
+ bool Expand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions = OrigNumExpansions;
+ if (getDerived().TryExpandParameterPacks(Ellipsis, Pattern.getSourceRange(),
+ Unexpanded, Expand, RetainExpansion,
+ NumExpansions))
+ return true;
- Outputs.addArgument(Out);
- }
+ TemplateArgumentLoc Out;
+ if (!Expand) {
+ // No opportunity for late expansion, return as is.
+ if (!TryExpandingSubstPacks) {
+ Outputs.addArgument(In);
+ return false;
+ }
+ // The transform has determined that we should perform a simple
+ // transformation on the pack expansion, producing another pack
+ // expansion.
+ TemplateArgumentLoc OutPattern;
+ std::optional<Sema::ArgPackSubstIndexRAII> SubstIndex(
+ std::in_place, getSema(), std::nullopt);
+ if (getDerived().TransformTemplateArgument(Pattern, OutPattern, Uneval))
+ return true;
- // If we're supposed to retain a pack expansion, do so by temporarily
- // forgetting the partially-substituted parameter pack.
- if (RetainExpansion) {
- ForgetPartiallySubstitutedPackRAII Forget(getDerived());
+ Out =
+ getDerived().RebuildPackExpansion(OutPattern, Ellipsis, NumExpansions);
+ if (Out.getArgument().isNull())
+ return true;
+ SubstIndex.reset();
+
+ // Some packs only know their lengths after substitution. We might be able
+ // to expand those here.
+ if (TryExpandingSubstPacks &&
+ OutPattern.getArgument().containsUnexpandedParameterPack()) {
----------------
ilya-biryukov wrote:
> Maybe we could move it to just after the first transform of template arguments inside TST, or possibly override the rebuild process for TST itself? Essentially, we're now just reusing TST to represent the novel "synthesized" packs (I haven't come up with a better name yet), and ideally we want to extend it / probably invent a new type node to support other pack forms outside of the template context, so not baking it into TemplateArgument transform makes some sense to me.
That was the direction I initially took in the previous attempt, i.e. the builtin can only be used in template arguments and gets processed right at the level of template arguments.
However, the new direction aligns better with what's being proposed in http://wg21.link/P3115R0 and paves the way to implement it in the future. One way to illustrate the differences can be illustrated is this example:
```cpp
template <class ...T> using unique_ptrs = tuple<__dedup<T...>*...>;
```
My initial version would forbid this code and would require to write something like:
```cpp
template <class ...T> struct TypeList {};
template <class ...T> struct AddPtrs {
using result = TypeList<T*...>;
};
template <class ...T> struct Unique {
using result = TypeList<T...>;
};
template <template<class...> class F, class TL> struct Apply;
template <template<class...> class F, class ...T> struct Apply<F, TypeList<T...>> {
using result = typename F<T...>::result;
};
template <class ...T> struct UniquePtrs {
using result = Apply<AddPtrs, typename Apply<Unique, TypeList<T...>>::result>::result;
};
template <class ...T> using unique_ptrs = UniquePtrs::result; // also convert to tuple.
```
The model proposed in http://wg21.link/P3115R0 does allow for much simple code in those situations, even though it requires a bit more implementation effort.
@cor3ntin is who proposed this direction, so I guess he can comment on the trade-offs.
https://github.com/llvm/llvm-project/pull/106730
More information about the cfe-commits
mailing list