Add optimization to sizeof...(X) handling: if none of parameter pack X's
corresponding arguments are unexpanded pack expansions, we can compute the result without substituting them. This significantly improves the memory usage and performance of make_integer_sequence implementations that do this kind of thing: using result = integer_sequence<T, Ns ..., sizeof...(Ns) + Ns ...>; ... but note that such an implementation will still perform O(sizeof...(Ns)^2) work while building the second pack expansion (we just have a somewhat lower constant now). In principle we could get this down to linear time by caching whether the number of expansions of a pack is constant, or checking whether we're within an alias template before scanning the pack for pack expansions (since that's the only case in which we do substitutions within a dependent context at the moment), but this patch doesn't attempt that. llvm-svn: 284653
This commit is contained in:
@@ -6499,6 +6499,14 @@ public:
|
||||
SourceLocation &Ellipsis,
|
||||
Optional<unsigned> &NumExpansions) const;
|
||||
|
||||
/// Given a template argument that contains an unexpanded parameter pack, but
|
||||
/// which has already been substituted, attempt to determine the number of
|
||||
/// elements that will be produced once this argument is fully-expanded.
|
||||
///
|
||||
/// This is intended for use when transforming 'sizeof...(Arg)' in order to
|
||||
/// avoid actually expanding the pack where possible.
|
||||
Optional<unsigned> getFullyPackExpandedSize(TemplateArgument Arg);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C++ Template Argument Deduction (C++ [temp.deduct])
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
@@ -639,7 +639,7 @@ bool Sema::CheckParameterPacksForExpansion(
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -936,6 +936,63 @@ Sema::getTemplateArgumentPackExpansionPattern(
|
||||
llvm_unreachable("Invalid TemplateArgument Kind!");
|
||||
}
|
||||
|
||||
Optional<unsigned> Sema::getFullyPackExpandedSize(TemplateArgument Arg) {
|
||||
assert(Arg.containsUnexpandedParameterPack());
|
||||
|
||||
// If this is a substituted pack, grab that pack. If not, we don't know
|
||||
// the size yet.
|
||||
// FIXME: We could find a size in more cases by looking for a substituted
|
||||
// pack anywhere within this argument, but that's not necessary in the common
|
||||
// case for 'sizeof...(A)' handling.
|
||||
TemplateArgument Pack;
|
||||
switch (Arg.getKind()) {
|
||||
case TemplateArgument::Type:
|
||||
if (auto *Subst = Arg.getAsType()->getAs<SubstTemplateTypeParmPackType>())
|
||||
Pack = Subst->getArgumentPack();
|
||||
else
|
||||
return None;
|
||||
break;
|
||||
|
||||
case TemplateArgument::Expression:
|
||||
if (auto *Subst =
|
||||
dyn_cast<SubstNonTypeTemplateParmPackExpr>(Arg.getAsExpr()))
|
||||
Pack = Subst->getArgumentPack();
|
||||
else if (auto *Subst = dyn_cast<FunctionParmPackExpr>(Arg.getAsExpr())) {
|
||||
for (ParmVarDecl *PD : *Subst)
|
||||
if (PD->isParameterPack())
|
||||
return None;
|
||||
return Subst->getNumExpansions();
|
||||
} else
|
||||
return None;
|
||||
break;
|
||||
|
||||
case TemplateArgument::Template:
|
||||
if (SubstTemplateTemplateParmPackStorage *Subst =
|
||||
Arg.getAsTemplate().getAsSubstTemplateTemplateParmPack())
|
||||
Pack = Subst->getArgumentPack();
|
||||
else
|
||||
return None;
|
||||
break;
|
||||
|
||||
case TemplateArgument::Declaration:
|
||||
case TemplateArgument::NullPtr:
|
||||
case TemplateArgument::TemplateExpansion:
|
||||
case TemplateArgument::Integral:
|
||||
case TemplateArgument::Pack:
|
||||
case TemplateArgument::Null:
|
||||
return None;
|
||||
}
|
||||
|
||||
// Check that no argument in the pack is itself a pack expansion.
|
||||
for (TemplateArgument Elem : Pack.pack_elements()) {
|
||||
// There's no point recursing in this case; we would have already
|
||||
// expanded this pack expansion into the enclosing pack if we could.
|
||||
if (Elem.isPackExpansion())
|
||||
return None;
|
||||
}
|
||||
return Pack.pack_size();
|
||||
}
|
||||
|
||||
static void CheckFoldOperand(Sema &S, Expr *E) {
|
||||
if (!E)
|
||||
return;
|
||||
|
||||
@@ -10763,6 +10763,51 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
|
||||
E->getRParenLoc(), None, None);
|
||||
}
|
||||
|
||||
// Try to compute the result without performing a partial substitution.
|
||||
Optional<unsigned> Result = 0;
|
||||
for (const TemplateArgument &Arg : PackArgs) {
|
||||
if (!Arg.isPackExpansion()) {
|
||||
Result = *Result + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
TemplateArgumentLoc ArgLoc;
|
||||
InventTemplateArgumentLoc(Arg, ArgLoc);
|
||||
|
||||
// Find the pattern of the pack expansion.
|
||||
SourceLocation Ellipsis;
|
||||
Optional<unsigned> OrigNumExpansions;
|
||||
TemplateArgumentLoc Pattern =
|
||||
getSema().getTemplateArgumentPackExpansionPattern(ArgLoc, Ellipsis,
|
||||
OrigNumExpansions);
|
||||
|
||||
// Substitute under the pack expansion. Do not expand the pack (yet).
|
||||
TemplateArgumentLoc OutPattern;
|
||||
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
|
||||
if (getDerived().TransformTemplateArgument(Pattern, OutPattern,
|
||||
/*Uneval*/ true))
|
||||
return true;
|
||||
|
||||
// See if we can determine the number of arguments from the result.
|
||||
Optional<unsigned> NumExpansions =
|
||||
getSema().getFullyPackExpandedSize(OutPattern.getArgument());
|
||||
if (!NumExpansions) {
|
||||
// No: we must be in an alias template expansion, and we're going to need
|
||||
// to actually expand the packs.
|
||||
Result = None;
|
||||
break;
|
||||
}
|
||||
|
||||
Result = *Result + *NumExpansions;
|
||||
}
|
||||
|
||||
// Common case: we could determine the number of expansions without
|
||||
// substituting.
|
||||
if (Result)
|
||||
return getDerived().RebuildSizeOfPackExpr(E->getOperatorLoc(), E->getPack(),
|
||||
E->getPackLoc(),
|
||||
E->getRParenLoc(), *Result, None);
|
||||
|
||||
TemplateArgumentListInfo TransformedPackArgs(E->getPackLoc(),
|
||||
E->getPackLoc());
|
||||
{
|
||||
@@ -10775,6 +10820,8 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
|
||||
return ExprError();
|
||||
}
|
||||
|
||||
// Check whether we managed to fully-expand the pack.
|
||||
// FIXME: Is it possible for us to do so and not hit the early exit path?
|
||||
SmallVector<TemplateArgument, 8> Args;
|
||||
bool PartialSubstitution = false;
|
||||
for (auto &Loc : TransformedPackArgs.arguments()) {
|
||||
|
||||
@@ -220,6 +220,23 @@ namespace PR14858 {
|
||||
|
||||
template<typename ...T, typename ...U> void h(X<T...> &) {}
|
||||
template<typename ...T, typename ...U> void h(X<U...> &) {} // ok, different
|
||||
|
||||
template<typename ...T> void i(auto (T ...t) -> int(&)[sizeof...(t)]);
|
||||
auto mk_arr(int, int) -> int(&)[2];
|
||||
void test_i() { i<int, int>(mk_arr); }
|
||||
|
||||
#if 0 // FIXME: This causes clang to assert.
|
||||
template<typename ...T> using Z = auto (T ...p) -> int (&)[sizeof...(p)];
|
||||
template<typename ...T, typename ...U> void j(Z<T..., U...> &) {}
|
||||
void test_j() { j<int, int>(mk_arr); }
|
||||
#endif
|
||||
|
||||
template<typename ...T> struct Q {
|
||||
template<typename ...U> using V = int[sizeof...(U)];
|
||||
template<typename ...U> void f(V<typename U::type..., typename T::type...> *);
|
||||
};
|
||||
struct B { typedef int type; };
|
||||
void test_q(int (&a)[5]) { Q<B, B, B>().f<B, B>(&a); }
|
||||
}
|
||||
|
||||
namespace redecl {
|
||||
|
||||
Reference in New Issue
Block a user