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:
Richard Smith
2016-10-19 22:18:42 +00:00
parent 01d6b963d2
commit c5452ed941
4 changed files with 130 additions and 1 deletions

View File

@@ -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])
//===--------------------------------------------------------------------===//

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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 {