Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit eff4af4

Browse files
committed
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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@284653 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent a32fe92 commit eff4af4

File tree

4 files changed

+130
-1
lines changed

4 files changed

+130
-1
lines changed

Diff for: include/clang/Sema/Sema.h

+8
Original file line numberDiff line numberDiff line change
@@ -6499,6 +6499,14 @@ class Sema {
64996499
SourceLocation &Ellipsis,
65006500
Optional<unsigned> &NumExpansions) const;
65016501

6502+
/// Given a template argument that contains an unexpanded parameter pack, but
6503+
/// which has already been substituted, attempt to determine the number of
6504+
/// elements that will be produced once this argument is fully-expanded.
6505+
///
6506+
/// This is intended for use when transforming 'sizeof...(Arg)' in order to
6507+
/// avoid actually expanding the pack where possible.
6508+
Optional<unsigned> getFullyPackExpandedSize(TemplateArgument Arg);
6509+
65026510
//===--------------------------------------------------------------------===//
65036511
// C++ Template Argument Deduction (C++ [temp.deduct])
65046512
//===--------------------------------------------------------------------===//

Diff for: lib/Sema/SemaTemplateVariadic.cpp

+58-1
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ bool Sema::CheckParameterPacksForExpansion(
639639
return true;
640640
}
641641
}
642-
642+
643643
return false;
644644
}
645645

@@ -936,6 +936,63 @@ Sema::getTemplateArgumentPackExpansionPattern(
936936
llvm_unreachable("Invalid TemplateArgument Kind!");
937937
}
938938

939+
Optional<unsigned> Sema::getFullyPackExpandedSize(TemplateArgument Arg) {
940+
assert(Arg.containsUnexpandedParameterPack());
941+
942+
// If this is a substituted pack, grab that pack. If not, we don't know
943+
// the size yet.
944+
// FIXME: We could find a size in more cases by looking for a substituted
945+
// pack anywhere within this argument, but that's not necessary in the common
946+
// case for 'sizeof...(A)' handling.
947+
TemplateArgument Pack;
948+
switch (Arg.getKind()) {
949+
case TemplateArgument::Type:
950+
if (auto *Subst = Arg.getAsType()->getAs<SubstTemplateTypeParmPackType>())
951+
Pack = Subst->getArgumentPack();
952+
else
953+
return None;
954+
break;
955+
956+
case TemplateArgument::Expression:
957+
if (auto *Subst =
958+
dyn_cast<SubstNonTypeTemplateParmPackExpr>(Arg.getAsExpr()))
959+
Pack = Subst->getArgumentPack();
960+
else if (auto *Subst = dyn_cast<FunctionParmPackExpr>(Arg.getAsExpr())) {
961+
for (ParmVarDecl *PD : *Subst)
962+
if (PD->isParameterPack())
963+
return None;
964+
return Subst->getNumExpansions();
965+
} else
966+
return None;
967+
break;
968+
969+
case TemplateArgument::Template:
970+
if (SubstTemplateTemplateParmPackStorage *Subst =
971+
Arg.getAsTemplate().getAsSubstTemplateTemplateParmPack())
972+
Pack = Subst->getArgumentPack();
973+
else
974+
return None;
975+
break;
976+
977+
case TemplateArgument::Declaration:
978+
case TemplateArgument::NullPtr:
979+
case TemplateArgument::TemplateExpansion:
980+
case TemplateArgument::Integral:
981+
case TemplateArgument::Pack:
982+
case TemplateArgument::Null:
983+
return None;
984+
}
985+
986+
// Check that no argument in the pack is itself a pack expansion.
987+
for (TemplateArgument Elem : Pack.pack_elements()) {
988+
// There's no point recursing in this case; we would have already
989+
// expanded this pack expansion into the enclosing pack if we could.
990+
if (Elem.isPackExpansion())
991+
return None;
992+
}
993+
return Pack.pack_size();
994+
}
995+
939996
static void CheckFoldOperand(Sema &S, Expr *E) {
940997
if (!E)
941998
return;

Diff for: lib/Sema/TreeTransform.h

+47
Original file line numberDiff line numberDiff line change
@@ -10763,6 +10763,51 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
1076310763
E->getRParenLoc(), None, None);
1076410764
}
1076510765

10766+
// Try to compute the result without performing a partial substitution.
10767+
Optional<unsigned> Result = 0;
10768+
for (const TemplateArgument &Arg : PackArgs) {
10769+
if (!Arg.isPackExpansion()) {
10770+
Result = *Result + 1;
10771+
continue;
10772+
}
10773+
10774+
TemplateArgumentLoc ArgLoc;
10775+
InventTemplateArgumentLoc(Arg, ArgLoc);
10776+
10777+
// Find the pattern of the pack expansion.
10778+
SourceLocation Ellipsis;
10779+
Optional<unsigned> OrigNumExpansions;
10780+
TemplateArgumentLoc Pattern =
10781+
getSema().getTemplateArgumentPackExpansionPattern(ArgLoc, Ellipsis,
10782+
OrigNumExpansions);
10783+
10784+
// Substitute under the pack expansion. Do not expand the pack (yet).
10785+
TemplateArgumentLoc OutPattern;
10786+
Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
10787+
if (getDerived().TransformTemplateArgument(Pattern, OutPattern,
10788+
/*Uneval*/ true))
10789+
return true;
10790+
10791+
// See if we can determine the number of arguments from the result.
10792+
Optional<unsigned> NumExpansions =
10793+
getSema().getFullyPackExpandedSize(OutPattern.getArgument());
10794+
if (!NumExpansions) {
10795+
// No: we must be in an alias template expansion, and we're going to need
10796+
// to actually expand the packs.
10797+
Result = None;
10798+
break;
10799+
}
10800+
10801+
Result = *Result + *NumExpansions;
10802+
}
10803+
10804+
// Common case: we could determine the number of expansions without
10805+
// substituting.
10806+
if (Result)
10807+
return getDerived().RebuildSizeOfPackExpr(E->getOperatorLoc(), E->getPack(),
10808+
E->getPackLoc(),
10809+
E->getRParenLoc(), *Result, None);
10810+
1076610811
TemplateArgumentListInfo TransformedPackArgs(E->getPackLoc(),
1076710812
E->getPackLoc());
1076810813
{
@@ -10775,6 +10820,8 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
1077510820
return ExprError();
1077610821
}
1077710822

10823+
// Check whether we managed to fully-expand the pack.
10824+
// FIXME: Is it possible for us to do so and not hit the early exit path?
1077810825
SmallVector<TemplateArgument, 8> Args;
1077910826
bool PartialSubstitution = false;
1078010827
for (auto &Loc : TransformedPackArgs.arguments()) {

Diff for: test/SemaTemplate/alias-templates.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,23 @@ namespace PR14858 {
220220

221221
template<typename ...T, typename ...U> void h(X<T...> &) {}
222222
template<typename ...T, typename ...U> void h(X<U...> &) {} // ok, different
223+
224+
template<typename ...T> void i(auto (T ...t) -> int(&)[sizeof...(t)]);
225+
auto mk_arr(int, int) -> int(&)[2];
226+
void test_i() { i<int, int>(mk_arr); }
227+
228+
#if 0 // FIXME: This causes clang to assert.
229+
template<typename ...T> using Z = auto (T ...p) -> int (&)[sizeof...(p)];
230+
template<typename ...T, typename ...U> void j(Z<T..., U...> &) {}
231+
void test_j() { j<int, int>(mk_arr); }
232+
#endif
233+
234+
template<typename ...T> struct Q {
235+
template<typename ...U> using V = int[sizeof...(U)];
236+
template<typename ...U> void f(V<typename U::type..., typename T::type...> *);
237+
};
238+
struct B { typedef int type; };
239+
void test_q(int (&a)[5]) { Q<B, B, B>().f<B, B>(&a); }
223240
}
224241

225242
namespace redecl {

0 commit comments

Comments
 (0)