[clang] [clang-tools-extra] [Clang] Add a builtin that deduplicate types into a pack (PR #106730)
Ilya Biryukov via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 5 05:41:04 PDT 2025
https://github.com/ilya-biryukov updated https://github.com/llvm/llvm-project/pull/106730
>From 2762d75194950cacc19f3a408eea58e81f989c6b Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Fri, 23 Aug 2024 17:27:26 +0200
Subject: [PATCH 1/2] [Clang] Add a builtins that deduplicate types into a pack
The new builtin `__builtin_dedup_pack` removes duplicates from list of types.
The added builtin is special in that they produce an unexpanded pack
in the spirit of P3115R0 proposal.
Produced packs can be used directly in template argument lists and get
immediately expanded as soon as results of the computation are
available.
It allows to easily combine them, e.g.:
```cpp
template <class ...T>
struct Normalize {
// Note: sort is not included in this PR, it illustrates the idea.
using result = std::tuple<
__builtin_sort_pack<
__builtin_dedup_pack<int, double, T...>...
>...>;
}
;
```
Limitations:
- only supported in template arguments and bases,
- can only be used inside the templates, even if non-dependent,
- the builtins cannot be assigned to template template parameters.
The actual implementation proceeds as follows:
- When the compiler encounters a `__builtin_dedup_pack` or other type-producing
builtin with dependent arguments, it creates a dependent
`TemplateSpecializationType`.
- During substitution, if the template arguments are non-dependent, we
will produce: a new type `SubstBuiltinTemplatePackType`, which stores
an argument pack that needs to be substituted. This type is similar to
the existing `SubstTemplateParmPack` in that it carries the argument
pack that needs to be expanded further. The relevant code is shared.
- On top of that, Clang also wraps the resulting type into
`TemplateSpecializationType`, but this time only as a sugar.
- To actually expand those packs, we collect the produced
`SubstBuiltinTemplatePackType` inside `CollectUnexpandedPacks`.
Because we know the size of the produces packs only after the initial
substitution, places that do the actual expansion will need to have a
second run over the substituted type to finalize the expansions (in
this patch we only support this for template arguments, see
`ExpandTemplateArgument`).
If the expansion are requested in the places we do not currently
support, we will produce an error.
More follow-up work will be needed to fully shape this:
- adding the builtin that sorts types,
- remove the restrictions for expansions,
- implementing P3115R0 (scheduled for C++29, see
https://github.com/cplusplus/papers/issues/2300).
---
.../clangd/unittests/FindTargetTests.cpp | 6 +
clang/include/clang/AST/ASTContext.h | 4 +
clang/include/clang/AST/DeclTemplate.h | 3 +
clang/include/clang/AST/RecursiveASTVisitor.h | 30 +-
clang/include/clang/AST/Type.h | 87 +++++-
clang/include/clang/AST/TypeLoc.h | 22 +-
clang/include/clang/AST/TypeProperties.td | 19 +-
clang/include/clang/Basic/BuiltinTemplates.td | 4 +
.../clang/Basic/DiagnosticSemaKinds.td | 7 +
clang/include/clang/Basic/TypeNodes.td | 4 +-
clang/include/clang/Sema/Sema.h | 9 +-
clang/include/clang/Sema/SemaInternal.h | 13 +-
.../clang/Serialization/TypeBitCodes.def | 1 +
clang/lib/AST/ASTContext.cpp | 32 +-
clang/lib/AST/ASTImporter.cpp | 8 +
clang/lib/AST/ASTStructuralEquivalence.cpp | 8 +
clang/lib/AST/DeclTemplate.cpp | 14 +-
clang/lib/AST/ItaniumMangle.cpp | 9 +
clang/lib/AST/MicrosoftMangle.cpp | 5 +
clang/lib/AST/Type.cpp | 82 +++--
clang/lib/AST/TypePrinter.cpp | 10 +
clang/lib/Parse/ParseTemplate.cpp | 13 +
clang/lib/Sema/SemaConcept.cpp | 12 +-
clang/lib/Sema/SemaDeclCXX.cpp | 7 +-
clang/lib/Sema/SemaTemplate.cpp | 71 +++++
clang/lib/Sema/SemaTemplateDeduction.cpp | 16 +-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 175 ++++++++---
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 44 ++-
clang/lib/Sema/SemaTemplateVariadic.cpp | 111 ++++++-
clang/lib/Sema/TreeTransform.h | 284 ++++++++++++------
clang/lib/Serialization/ASTReader.cpp | 5 +
clang/lib/Serialization/ASTWriter.cpp | 6 +
.../test/Import/builtin-template/Inputs/S.cpp | 7 +
clang/test/Import/builtin-template/test.cpp | 10 +-
clang/test/PCH/dedup_types.cpp | 20 ++
clang/test/SemaCXX/pr100095.cpp | 1 -
.../test/SemaTemplate/dedup-types-builtin.cpp | 226 ++++++++++++++
clang/tools/libclang/CIndex.cpp | 1 +
38 files changed, 1156 insertions(+), 230 deletions(-)
create mode 100644 clang/test/PCH/dedup_types.cpp
create mode 100644 clang/test/SemaTemplate/dedup-types-builtin.cpp
diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index 4d77f9d690ca0..68df9b75de74a 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -731,6 +731,12 @@ TEST_F(TargetDeclTest, BuiltinTemplates) {
using type_pack_element = [[__type_pack_element]]<N, Pack...>;
)cpp";
EXPECT_DECLS("TemplateSpecializationTypeLoc", );
+
+ Code = R"cpp(
+ template <template <class...> class Templ, class... Types>
+ using dedup_types = Templ<[[__builtin_dedup_pack]]<Types...>...>;
+ )cpp";
+ EXPECT_DECLS("TemplateSpecializationTypeLoc", );
}
TEST_F(TargetDeclTest, MemberOfTemplate) {
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 99f54305d8ed6..876577b1e14d6 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -25,6 +25,7 @@
#include "clang/AST/RawCommentList.h"
#include "clang/AST/SYCLKernelInfo.h"
#include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceLocation.h"
@@ -230,6 +231,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
SubstTemplateTypeParmTypes;
mutable llvm::FoldingSet<SubstTemplateTypeParmPackType>
SubstTemplateTypeParmPackTypes;
+ mutable llvm::FoldingSet<SubstBuiltinTemplatePackType>
+ SubstBuiltinTemplatePackTypes;
mutable llvm::ContextualFoldingSet<TemplateSpecializationType, ASTContext&>
TemplateSpecializationTypes;
mutable llvm::FoldingSet<ParenType> ParenTypes{GeneralTypesLog2InitSize};
@@ -1876,6 +1879,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
unsigned Index, bool Final,
const TemplateArgument &ArgPack);
+ QualType getSubstBuiltinTemplatePack(const TemplateArgument &ArgPack);
QualType
getTemplateTypeParmType(unsigned Depth, unsigned Index,
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 32de203f2d831..af402938a0f49 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -1798,6 +1798,9 @@ class BuiltinTemplateDecl : public TemplateDecl {
BuiltinTemplateKind getBuiltinTemplateKind() const { return BTK; }
};
+bool isPackProducingBuiltinTemplate(const TemplateDecl *D);
+bool isPackProducingBuiltinTemplateName(TemplateName N);
+
/// Provides information about an explicit instantiation of a variable or class
/// template.
struct ExplicitInstantiationInfo {
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 62991d986e675..00fa88aea2611 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -490,6 +490,8 @@ template <typename Derived> class RecursiveASTVisitor {
bool TraverseTemplateArgumentLocsHelper(const TemplateArgumentLoc *TAL,
unsigned Count);
bool TraverseArrayTypeLocHelper(ArrayTypeLoc TL);
+ bool TraverseSubstPackTypeHelper(SubstPackType *T);
+ bool TraverseSubstPackTypeLocHelper(SubstPackTypeLoc TL);
bool TraverseRecordHelper(RecordDecl *D);
bool TraverseCXXRecordHelper(CXXRecordDecl *D);
bool TraverseDeclaratorHelper(DeclaratorDecl *D);
@@ -1126,9 +1128,10 @@ DEF_TRAVERSE_TYPE(TemplateTypeParmType, {})
DEF_TRAVERSE_TYPE(SubstTemplateTypeParmType, {
TRY_TO(TraverseType(T->getReplacementType()));
})
-DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType, {
- TRY_TO(TraverseTemplateArgument(T->getArgumentPack()));
-})
+DEF_TRAVERSE_TYPE(SubstTemplateTypeParmPackType,
+ { TRY_TO(TraverseSubstPackTypeHelper(T)); })
+DEF_TRAVERSE_TYPE(SubstBuiltinTemplatePackType,
+ { TRY_TO(TraverseSubstPackTypeHelper(T)); })
DEF_TRAVERSE_TYPE(TemplateSpecializationType, {
TRY_TO(TraverseTemplateName(T->getTemplateName()));
@@ -1434,9 +1437,26 @@ DEF_TRAVERSE_TYPELOC(TemplateTypeParmType, {})
DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmType, {
TRY_TO(TraverseType(TL.getTypePtr()->getReplacementType()));
})
-DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType, {
+
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseSubstPackTypeLocHelper(
+ SubstPackTypeLoc TL) {
TRY_TO(TraverseTemplateArgument(TL.getTypePtr()->getArgumentPack()));
-})
+ return true;
+}
+
+template <typename Derived>
+bool RecursiveASTVisitor<Derived>::TraverseSubstPackTypeHelper(
+ SubstPackType *T) {
+ TRY_TO(TraverseTemplateArgument(T->getArgumentPack()));
+ return true;
+}
+
+DEF_TRAVERSE_TYPELOC(SubstTemplateTypeParmPackType,
+ { TRY_TO(TraverseSubstPackTypeLocHelper(TL)); })
+
+DEF_TRAVERSE_TYPELOC(SubstBuiltinTemplatePackType,
+ { TRY_TO(TraverseSubstPackTypeLocHelper(TL)); })
// FIXME: use the loc for the template name?
DEF_TRAVERSE_TYPELOC(TemplateSpecializationType, {
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 12dce309127e5..692cacb1061cd 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2186,21 +2186,35 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
unsigned PackIndex : 15;
};
- class SubstTemplateTypeParmPackTypeBitfields {
- friend class SubstTemplateTypeParmPackType;
+ class SubstPackTypeBitfields {
+ friend class SubstPackType;
LLVM_PREFERRED_TYPE(TypeBitfields)
unsigned : NumTypeBits;
- // The index of the template parameter this substitution represents.
- unsigned Index : 16;
-
/// The number of template arguments in \c Arguments, which is
/// expected to be able to hold at least 1024 according to [implimits].
/// However as this limit is somewhat easy to hit with template
/// metaprogramming we'd prefer to keep it as large as possible.
unsigned NumArgs : 16;
};
+ // FIXME: idiomatically, we want to have exact numbers, but that
+ // ends up generating incorrect code (writes to
+ // SubstTemplateTypeParmPackTypeBitfields.Index also update
+ // bits in NumArgs).
+ // enum { NumSubstPackTypeBits = NumTypeBits + 16 };
+ static_assert(NumTypeBits + 16 <= 48);
+ enum { NumSubstPackTypeBits = 48 };
+
+ class SubstTemplateTypeParmPackTypeBitfields {
+ friend class SubstTemplateTypeParmPackType;
+
+ LLVM_PREFERRED_TYPE(SubstPackTypeBitfields)
+ unsigned : NumSubstPackTypeBits;
+
+ // The index of the template parameter this substitution represents.
+ unsigned Index : 16;
+ };
class TemplateSpecializationTypeBitfields {
friend class TemplateSpecializationType;
@@ -2315,6 +2329,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
VectorTypeBitfields VectorTypeBits;
TemplateTypeParmTypeBitfields TemplateTypeParmTypeBits;
SubstTemplateTypeParmTypeBitfields SubstTemplateTypeParmTypeBits;
+ SubstPackTypeBitfields SubstPackTypeBits;
SubstTemplateTypeParmPackTypeBitfields SubstTemplateTypeParmPackTypeBits;
TemplateSpecializationTypeBitfields TemplateSpecializationTypeBits;
DependentTemplateSpecializationTypeBitfields
@@ -6654,6 +6669,56 @@ class SubstTemplateTypeParmType final
}
};
+/// Represents the result of substituting a set of types as a template argument
+/// that needs to be expanded later.
+///
+/// These types are always dependent and produced depending on the situations:
+/// - SubstTemplateTypeParmPack is an expansion that had to be delayed,
+/// - SubstBuiltinTemplatePackType is an expansion from a builtin.
+class SubstPackType : public Type, public llvm::FoldingSetNode {
+ friend class ASTContext;
+
+ /// A pointer to the set of template arguments that this
+ /// parameter pack is instantiated with.
+ const TemplateArgument *Arguments;
+
+protected:
+ SubstPackType(TypeClass Derived, QualType Canon,
+ const TemplateArgument &ArgPack);
+
+public:
+ unsigned getNumArgs() const { return SubstPackTypeBits.NumArgs; }
+
+ TemplateArgument getArgumentPack() const;
+
+ void Profile(llvm::FoldingSetNodeID &ID);
+ static void Profile(llvm::FoldingSetNodeID &ID,
+ const TemplateArgument &ArgPack);
+
+ static bool classof(const Type *T) {
+ return T->getTypeClass() == SubstTemplateTypeParmPack ||
+ T->getTypeClass() == SubstBuiltinTemplatePack;
+ }
+};
+
+/// Represents the result of substituting a builtin template as a pack.
+class SubstBuiltinTemplatePackType : public SubstPackType {
+ friend class ASTContext;
+
+ SubstBuiltinTemplatePackType(QualType Canon, const TemplateArgument &ArgPack);
+
+public:
+ bool isSugared() const { return false; }
+ QualType desugar() const { return QualType(this, 0); }
+
+ /// Mark that we reuse the Profile. We do not introduce new fields.
+ using SubstPackType::Profile;
+
+ static bool classof(const Type *T) {
+ return T->getTypeClass() == SubstBuiltinTemplatePack;
+ }
+};
+
/// Represents the result of substituting a set of types for a template
/// type parameter pack.
///
@@ -6666,7 +6731,7 @@ class SubstTemplateTypeParmType final
/// that pack expansion (e.g., when all template parameters have corresponding
/// arguments), this type will be replaced with the \c SubstTemplateTypeParmType
/// at the current pack substitution index.
-class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {
+class SubstTemplateTypeParmPackType : public SubstPackType {
friend class ASTContext;
/// A pointer to the set of template arguments that this
@@ -6698,15 +6763,9 @@ class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {
// sugared: it doesn't need to be resugared later.
bool getFinal() const;
- unsigned getNumArgs() const {
- return SubstTemplateTypeParmPackTypeBits.NumArgs;
- }
-
bool isSugared() const { return false; }
QualType desugar() const { return QualType(this, 0); }
- TemplateArgument getArgumentPack() const;
-
void Profile(llvm::FoldingSetNodeID &ID);
static void Profile(llvm::FoldingSetNodeID &ID, const Decl *AssociatedDecl,
unsigned Index, bool Final,
@@ -6936,9 +6995,7 @@ class TemplateSpecializationType : public Type, public llvm::FoldingSetNode {
TemplateSpecializationTypeBits.NumArgs};
}
- bool isSugared() const {
- return !isDependentType() || isCurrentInstantiation() || isTypeAlias();
- }
+ bool isSugared() const;
QualType desugar() const {
return isTypeAlias() ? getAliasedType() : getCanonicalTypeInternal();
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 52ef7ac54145e..0929bc4ec259d 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -859,12 +859,22 @@ class SubstTemplateTypeParmTypeLoc :
SubstTemplateTypeParmType> {
};
- /// Wrapper for substituted template type parameters.
-class SubstTemplateTypeParmPackTypeLoc :
- public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
- SubstTemplateTypeParmPackTypeLoc,
- SubstTemplateTypeParmPackType> {
-};
+/// Abstract type representing delayed type pack expansions.
+class SubstPackTypeLoc
+ : public InheritingConcreteTypeLoc<TypeSpecTypeLoc, SubstPackTypeLoc,
+ SubstPackType> {};
+
+/// Wrapper for substituted template type parameters.
+class SubstTemplateTypeParmPackTypeLoc
+ : public InheritingConcreteTypeLoc<SubstPackTypeLoc,
+ SubstTemplateTypeParmPackTypeLoc,
+ SubstTemplateTypeParmPackType> {};
+
+/// Wrapper for substituted template type parameters.
+class SubstBuiltinTemplatePackTypeLoc
+ : public InheritingConcreteTypeLoc<SubstPackTypeLoc,
+ SubstBuiltinTemplatePackTypeLoc,
+ SubstBuiltinTemplatePackType> {};
struct AttributedLocInfo {
const Attr *TypeAttr;
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 3373e963038f1..92470c41fbce5 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -848,6 +848,12 @@ let Class = PackExpansionType in {
}]>;
}
+let Class = SubstPackType in {
+ def : Property<"replacementPack", TemplateArgument> {
+ let Read = [{ node->getArgumentPack() }];
+ }
+}
+
let Class = SubstTemplateTypeParmPackType in {
def : Property<"associatedDecl", DeclRef> {
let Read = [{ node->getAssociatedDecl() }];
@@ -855,12 +861,7 @@ let Class = SubstTemplateTypeParmPackType in {
def : Property<"Index", UInt32> {
let Read = [{ node->getIndex() }];
}
- def : Property<"Final", Bool> {
- let Read = [{ node->getFinal() }];
- }
- def : Property<"replacementPack", TemplateArgument> {
- let Read = [{ node->getArgumentPack() }];
- }
+ def : Property<"Final", Bool> { let Read = [{ node->getFinal() }]; }
def : Creator<[{
return ctx.getSubstTemplateTypeParmPackType(
@@ -868,6 +869,12 @@ let Class = SubstTemplateTypeParmPackType in {
}]>;
}
+let Class = SubstBuiltinTemplatePackType in {
+ def : Creator<[{
+ return ctx.getSubstBuiltinTemplatePack(replacementPack);
+ }]>;
+}
+
let Class = BuiltinType in {
def : Property<"kind", BuiltinTypeKind> {
let Read = [{ node->getKind() }];
diff --git a/clang/include/clang/Basic/BuiltinTemplates.td b/clang/include/clang/Basic/BuiltinTemplates.td
index 5b9672b395955..504405acbdc78 100644
--- a/clang/include/clang/Basic/BuiltinTemplates.td
+++ b/clang/include/clang/Basic/BuiltinTemplates.td
@@ -62,3 +62,7 @@ def __builtin_common_type : CPlusPlusBuiltinTemplate<
// typename ...Operands>
def __hlsl_spirv_type : HLSLBuiltinTemplate<
[Uint32T, Uint32T, Uint32T, Class<"Operands", /*is_variadic=*/1>]>;
+
+// template <class ...Args>
+def __builtin_dedup_pack
+ : CPlusPlusBuiltinTemplate<[Class<"Args", /*is_variadic=*/1>]>;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f903b7f4dacd0..205bced4684da 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6074,6 +6074,13 @@ def warn_cxx23_pack_indexing : Warning<
def err_pack_outside_template : Error<
"pack declaration outside of template">;
+def err_builtin_pack_outside_template
+ : Error<"builtin returning packs used outside of template">;
+
+def err_unsupported_builtin_template_pack_position
+ : Error<"expansions of %0 are not supported here. Only expansions in "
+ "template arguments and class bases are supported">;
+
def err_fold_expression_packs_both_sides : Error<
"binary fold expression has unexpanded parameter packs in both operands">;
def err_fold_expression_empty : Error<
diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td
index 971ce541d4831..6698465a6d56b 100644
--- a/clang/include/clang/Basic/TypeNodes.td
+++ b/clang/include/clang/Basic/TypeNodes.td
@@ -97,7 +97,9 @@ def HLSLAttributedResourceType : TypeNode<Type>;
def HLSLInlineSpirvType : TypeNode<Type>;
def TemplateTypeParmType : TypeNode<Type>, AlwaysDependent, LeafType;
def SubstTemplateTypeParmType : TypeNode<Type>, NeverCanonical;
-def SubstTemplateTypeParmPackType : TypeNode<Type>, AlwaysDependent;
+def SubstPackType : TypeNode<Type, 1>;
+def SubstTemplateTypeParmPackType : TypeNode<SubstPackType>, AlwaysDependent;
+def SubstBuiltinTemplatePackType : TypeNode<SubstPackType>, AlwaysDependent;
def TemplateSpecializationType : TypeNode<Type>, NeverCanonicalUnlessDependent;
def DeducedType : TypeNode<Type, 1>;
def AutoType : TypeNode<DeducedType>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5211373367677..1cdc2aabacf42 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -228,7 +228,9 @@ void threadSafetyCleanup(BeforeSet *Cache);
// FIXME: No way to easily map from TemplateTypeParmTypes to
// TemplateTypeParmDecls, so we have this horrible PointerUnion.
-typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *>,
+typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType *, NamedDecl *,
+ const TemplateSpecializationType *,
+ const SubstBuiltinTemplatePackType *>,
SourceLocation>
UnexpandedParameterPack;
@@ -13487,8 +13489,6 @@ class Sema final : public SemaBase {
~ArgPackSubstIndexRAII() { Self.ArgPackSubstIndex = OldSubstIndex; }
};
- friend class ArgumentPackSubstitutionRAII;
-
void pushCodeSynthesisContext(CodeSynthesisContext Ctx);
void popCodeSynthesisContext();
@@ -14504,7 +14504,8 @@ class Sema final : public SemaBase {
bool CheckParameterPacksForExpansion(
SourceLocation EllipsisLoc, SourceRange PatternRange,
ArrayRef<UnexpandedParameterPack> Unexpanded,
- const MultiLevelTemplateArgumentList &TemplateArgs, bool &ShouldExpand,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ bool FailOnPackProducingTemplates, bool &ShouldExpand,
bool &RetainExpansion, UnsignedOrNone &NumExpansions);
/// Determine the number of arguments in the given pack expansion
diff --git a/clang/include/clang/Sema/SemaInternal.h b/clang/include/clang/Sema/SemaInternal.h
index 42c9469e44e53..8360a1f303960 100644
--- a/clang/include/clang/Sema/SemaInternal.h
+++ b/clang/include/clang/Sema/SemaInternal.h
@@ -15,7 +15,9 @@
#define LLVM_CLANG_SEMA_SEMAINTERNAL_H
#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Type.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaDiagnostic.h"
@@ -71,12 +73,17 @@ inline std::pair<unsigned, unsigned> getDepthAndIndex(const NamedDecl *ND) {
}
/// Retrieve the depth and index of an unexpanded parameter pack.
-inline std::pair<unsigned, unsigned>
+/// Returns nullopt when the unexpanded packs do not correspond to template
+/// parameters, e.g. __builtin_dedup_types.
+inline std::optional<std::pair<unsigned, unsigned>>
getDepthAndIndex(UnexpandedParameterPack UPP) {
if (const auto *TTP = dyn_cast<const TemplateTypeParmType *>(UPP.first))
return std::make_pair(TTP->getDepth(), TTP->getIndex());
-
- return getDepthAndIndex(cast<NamedDecl *>(UPP.first));
+ if (isa<NamedDecl *>(UPP.first))
+ return getDepthAndIndex(cast<NamedDecl *>(UPP.first));
+ assert(isa<const TemplateSpecializationType *>(UPP.first) ||
+ isa<const SubstBuiltinTemplatePackType *>(UPP.first));
+ return std::nullopt;
}
class TypoCorrectionConsumer : public VisibleDeclConsumer {
diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def
index 613eb6af2005a..1cfc31c570f05 100644
--- a/clang/include/clang/Serialization/TypeBitCodes.def
+++ b/clang/include/clang/Serialization/TypeBitCodes.def
@@ -70,5 +70,6 @@ TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58)
TYPE_BIT_CODE(HLSLAttributedResource, HLSLRESOURCE_ATTRIBUTED, 59)
TYPE_BIT_CODE(HLSLInlineSpirv, HLSL_INLINE_SPIRV, 60)
TYPE_BIT_CODE(PredefinedSugar, PREDEFINED_SUGAR, 61)
+TYPE_BIT_CODE(SubstBuiltinTemplatePack, SUBST_BUILTIN_TEMPLATE_PACK, 62)
#undef TYPE_BIT_CODE
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 3a16111dd5f7d..846e8c7d98503 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -4306,6 +4306,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const {
case Type::DependentTemplateSpecialization:
case Type::TemplateTypeParm:
case Type::SubstTemplateTypeParmPack:
+ case Type::SubstBuiltinTemplatePack:
case Type::Auto:
case Type::DeducedTemplateSpecialization:
case Type::PackExpansion:
@@ -5655,7 +5656,6 @@ QualType ASTContext::getSubstTemplateTypeParmType(QualType Replacement,
return QualType(SubstParm, 0);
}
-/// Retrieve a
QualType
ASTContext::getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
unsigned Index, bool Final,
@@ -5694,6 +5694,35 @@ ASTContext::getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
return QualType(SubstParm, 0);
}
+QualType
+ASTContext::getSubstBuiltinTemplatePack(const TemplateArgument &ArgPack) {
+#ifndef NDEBUG
+ for (const auto &P : ArgPack.pack_elements())
+ assert(P.getKind() == TemplateArgument::Type && "Pack contains a non-type");
+#endif
+
+ llvm::FoldingSetNodeID ID;
+ SubstBuiltinTemplatePackType::Profile(ID, ArgPack);
+
+ void *InsertPos = nullptr;
+ if (auto *T =
+ SubstBuiltinTemplatePackTypes.FindNodeOrInsertPos(ID, InsertPos))
+ return QualType(T, 0);
+
+ QualType Canon;
+ {
+ TemplateArgument CanonArgPack = getCanonicalTemplateArgument(ArgPack);
+ if (!CanonArgPack.structurallyEquals(ArgPack))
+ Canon = getSubstBuiltinTemplatePack(CanonArgPack);
+ }
+
+ auto *PackType = new (*this, alignof(SubstBuiltinTemplatePackType))
+ SubstBuiltinTemplatePackType(Canon, ArgPack);
+ Types.push_back(PackType);
+ SubstBuiltinTemplatePackTypes.InsertNode(PackType, InsertPos);
+ return QualType(PackType, 0);
+}
+
/// Retrieve the template type parameter type for a template
/// parameter or parameter pack with the given depth, index, and (optionally)
/// name.
@@ -13987,6 +14016,7 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
SUGAR_FREE_TYPE(ObjCInterface)
SUGAR_FREE_TYPE(Record)
SUGAR_FREE_TYPE(SubstTemplateTypeParmPack)
+ SUGAR_FREE_TYPE(SubstBuiltinTemplatePack)
SUGAR_FREE_TYPE(UnresolvedUsing)
SUGAR_FREE_TYPE(HLSLAttributedResource)
SUGAR_FREE_TYPE(HLSLInlineSpirv)
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 8e2927bdc8d6f..cdb4ec4736b2d 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1834,6 +1834,14 @@ ExpectedType ASTNodeImporter::VisitSubstTemplateTypeParmPackType(
*ReplacedOrErr, T->getIndex(), T->getFinal(), *ToArgumentPack);
}
+ExpectedType ASTNodeImporter::VisitSubstBuiltinTemplatePackType(
+ const SubstBuiltinTemplatePackType *T) {
+ Expected<TemplateArgument> ToArgumentPack = import(T->getArgumentPack());
+ if (!ToArgumentPack)
+ return ToArgumentPack.takeError();
+ return Importer.getToContext().getSubstBuiltinTemplatePack(*ToArgumentPack);
+}
+
ExpectedType ASTNodeImporter::VisitTemplateSpecializationType(
const TemplateSpecializationType *T) {
auto ToTemplateOrErr = import(T->getTemplateName());
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 0e2023f86c19d..5bd7832524e18 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1322,6 +1322,14 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
break;
}
+ case Type::SubstBuiltinTemplatePack: {
+ const auto *Subst1 = cast<SubstBuiltinTemplatePackType>(T1);
+ const auto *Subst2 = cast<SubstBuiltinTemplatePackType>(T2);
+ if (!IsStructurallyEquivalent(Context, Subst1->getArgumentPack(),
+ Subst2->getArgumentPack()))
+ return false;
+ break;
+ }
case Type::SubstTemplateTypeParmPack: {
const auto *Subst1 = cast<SubstTemplateTypeParmPackType>(T1);
const auto *Subst2 = cast<SubstTemplateTypeParmPackType>(T2);
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index bc4a299e67f7c..38047c36003ee 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -275,6 +275,17 @@ void *allocateDefaultArgStorageChain(const ASTContext &C) {
return new (C) char[sizeof(void*) * 2];
}
+bool isPackProducingBuiltinTemplate(const TemplateDecl *D) {
+ auto *BD = llvm::dyn_cast<BuiltinTemplateDecl>(D);
+ return BD && BD->getBuiltinTemplateKind() == clang::BTK__builtin_dedup_pack;
+}
+
+bool isPackProducingBuiltinTemplateName(TemplateName N) {
+ if (N.getKind() == TemplateName::DeducedTemplate)
+ return false;
+ auto *T = N.getTemplateDeclAndDefaultArgs().first;
+ return T && isPackProducingBuiltinTemplate(T);
+}
} // namespace clang
//===----------------------------------------------------------------------===//
@@ -307,8 +318,9 @@ bool TemplateDecl::hasAssociatedConstraints() const {
bool TemplateDecl::isTypeAlias() const {
switch (getKind()) {
case TemplateDecl::TypeAliasTemplate:
- case TemplateDecl::BuiltinTemplate:
return true;
+ case TemplateDecl::BuiltinTemplate:
+ return !isPackProducingBuiltinTemplate(this);
default:
return false;
};
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 5233648d8f9c8..8bd812cec3556 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2470,6 +2470,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
case Type::CountAttributed:
llvm_unreachable("type is illegal as a nested name specifier");
+ case Type::SubstBuiltinTemplatePack:
case Type::SubstTemplateTypeParmPack:
// FIXME: not clear how to mangle this!
// template <class T...> class A {
@@ -3924,6 +3925,14 @@ void CXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T) {
Out << "_SUBSTPACK_";
}
+void CXXNameMangler::mangleType(const SubstBuiltinTemplatePackType *T) {
+ // FIXME: not clear how to mangle this!
+ // template <class T...> class A {
+ // template <class U...> void foo(__builtin_dedup_pack<T...>(*)(U) x...);
+ // };
+ Out << "_SUBSTPACK_";
+}
+
// <type> ::= P <type> # pointer-to
void CXXNameMangler::mangleType(const PointerType *T) {
Out << 'P';
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index e6ea0ada2e9ec..8988f5aa73bb4 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3384,6 +3384,11 @@ void MicrosoftCXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T,
Error(Range.getBegin(), "substituted parameter pack") << Range;
}
+void MicrosoftCXXNameMangler::mangleType(const SubstBuiltinTemplatePackType *T,
+ Qualifiers, SourceRange Range) {
+ Error(Range.getBegin(), "substituted builtin template pack") << Range;
+}
+
// <type> ::= <pointer-type>
// <pointer-type> ::= E? <pointer-cvr-qualifiers> <cvr-qualifiers> <type>
// # the E is required for 64-bit non-static pointers
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 141edc881d683..c2f07184fb801 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4393,17 +4393,47 @@ void SubstTemplateTypeParmType::Profile(llvm::FoldingSetNodeID &ID,
ID.AddBoolean(Final);
}
+SubstPackType::SubstPackType(TypeClass Derived, QualType Canon,
+ const TemplateArgument &ArgPack)
+ : Type(Derived, Canon,
+ TypeDependence::DependentInstantiation |
+ TypeDependence::UnexpandedPack),
+ Arguments(ArgPack.pack_begin()) {
+#ifndef NDEBUG
+ for (const auto &P : ArgPack.pack_elements()) {
+ assert(P.getKind() == TemplateArgument::Type &&
+ "non-type argument to SubstPackType?");
+ }
+#endif
+ SubstPackTypeBits.NumArgs = ArgPack.pack_size();
+}
+
+TemplateArgument SubstPackType::getArgumentPack() const {
+ return TemplateArgument(llvm::ArrayRef(Arguments, getNumArgs()));
+}
+
+void SubstPackType::Profile(llvm::FoldingSetNodeID &ID) {
+ Profile(ID, getArgumentPack());
+}
+
+void SubstPackType::Profile(llvm::FoldingSetNodeID &ID,
+ const TemplateArgument &ArgPack) {
+ ID.AddInteger(ArgPack.pack_size());
+ for (const auto &P : ArgPack.pack_elements())
+ ID.AddPointer(P.getAsType().getAsOpaquePtr());
+}
+
SubstTemplateTypeParmPackType::SubstTemplateTypeParmPackType(
QualType Canon, Decl *AssociatedDecl, unsigned Index, bool Final,
const TemplateArgument &ArgPack)
- : Type(SubstTemplateTypeParmPack, Canon,
- TypeDependence::DependentInstantiation |
- TypeDependence::UnexpandedPack),
- Arguments(ArgPack.pack_begin()),
+ : SubstPackType(SubstTemplateTypeParmPack, Canon, ArgPack),
AssociatedDeclAndFinal(AssociatedDecl, Final) {
- SubstTemplateTypeParmPackTypeBits.Index = Index;
- SubstTemplateTypeParmPackTypeBits.NumArgs = ArgPack.pack_size();
assert(AssociatedDecl != nullptr);
+
+ SubstTemplateTypeParmPackTypeBits.Index = Index;
+ assert(getNumArgs() == ArgPack.pack_size() &&
+ "Parent bitfields in SubstPackType were overwritten."
+ "Check NumSubstPackTypeBits.");
}
Decl *SubstTemplateTypeParmPackType::getAssociatedDecl() const {
@@ -4423,10 +4453,6 @@ IdentifierInfo *SubstTemplateTypeParmPackType::getIdentifier() const {
return getReplacedParameter()->getIdentifier();
}
-TemplateArgument SubstTemplateTypeParmPackType::getArgumentPack() const {
- return TemplateArgument(llvm::ArrayRef(Arguments, getNumArgs()));
-}
-
void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getAssociatedDecl(), getIndex(), getFinal(), getArgumentPack());
}
@@ -4438,11 +4464,13 @@ void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID,
ID.AddPointer(AssociatedDecl);
ID.AddInteger(Index);
ID.AddBoolean(Final);
- ID.AddInteger(ArgPack.pack_size());
- for (const auto &P : ArgPack.pack_elements())
- ID.AddPointer(P.getAsType().getAsOpaquePtr());
+ SubstPackType::Profile(ID, ArgPack);
}
+SubstBuiltinTemplatePackType::SubstBuiltinTemplatePackType(
+ QualType Canon, const TemplateArgument &ArgPack)
+ : SubstPackType(SubstBuiltinTemplatePack, Canon, ArgPack) {}
+
bool TemplateSpecializationType::anyDependentTemplateArguments(
const TemplateArgumentListInfo &Args,
ArrayRef<TemplateArgument> Converted) {
@@ -4466,17 +4494,28 @@ bool TemplateSpecializationType::anyInstantiationDependentTemplateArguments(
return false;
}
+static TypeDependence GetTemplateSpecializationTypeDep(QualType Underlying,
+ TemplateName T) {
+ TypeDependence D = Underlying.isNull()
+ ? TypeDependence::DependentInstantiation
+ : toSemanticDependence(Underlying->getDependence());
+ D |= toTypeDependence(T.getDependence()) & TypeDependence::UnexpandedPack;
+ if (isPackProducingBuiltinTemplateName(T)) {
+ if (Underlying.isNull()) // Dependent, will produce a pack on substitution.
+ D |= TypeDependence::UnexpandedPack;
+ else
+ D |= (Underlying->getDependence() & TypeDependence::UnexpandedPack);
+ }
+ return D;
+}
+
TemplateSpecializationType::TemplateSpecializationType(
TemplateName T, bool IsAlias, ArrayRef<TemplateArgument> Args,
QualType Underlying)
: Type(TemplateSpecialization,
Underlying.isNull() ? QualType(this, 0)
: Underlying.getCanonicalType(),
- (Underlying.isNull()
- ? TypeDependence::DependentInstantiation
- : toSemanticDependence(Underlying->getDependence())) |
- (toTypeDependence(T.getDependence()) &
- TypeDependence::UnexpandedPack)),
+ GetTemplateSpecializationTypeDep(Underlying, T)),
Template(T) {
TemplateSpecializationTypeBits.NumArgs = Args.size();
TemplateSpecializationTypeBits.TypeAlias = IsAlias;
@@ -4522,6 +4561,12 @@ QualType TemplateSpecializationType::getAliasedType() const {
return *reinterpret_cast<const QualType *>(template_arguments().end());
}
+bool clang::TemplateSpecializationType::isSugared() const {
+ return !isDependentType() || isCurrentInstantiation() || isTypeAlias() ||
+ (isPackProducingBuiltinTemplateName(Template) &&
+ llvm::isa<SubstBuiltinTemplatePackType>(*getCanonicalTypeInternal()));
+}
+
void TemplateSpecializationType::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &Ctx) {
Profile(ID, Template, template_arguments(),
@@ -4938,6 +4983,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
case Type::UnaryTransform:
case Type::TemplateTypeParm:
case Type::SubstTemplateTypeParmPack:
+ case Type::SubstBuiltinTemplatePack:
case Type::DependentName:
case Type::DependentTemplateSpecialization:
case Type::Auto:
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index deb453fe6ee75..a7ef675a6b427 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -233,6 +233,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
case Type::Elaborated:
case Type::TemplateTypeParm:
case Type::SubstTemplateTypeParmPack:
+ case Type::SubstBuiltinTemplatePack:
case Type::DeducedTemplateSpecialization:
case Type::TemplateSpecialization:
case Type::InjectedClassName:
@@ -1640,6 +1641,15 @@ void TypePrinter::printSubstTemplateTypeParmAfter(
printAfter(T->getReplacementType(), OS);
}
+void TypePrinter::printSubstBuiltinTemplatePackBefore(
+ const SubstBuiltinTemplatePackType *T, raw_ostream &OS) {
+ IncludeStrongLifetimeRAII Strong(Policy);
+ OS << "type-pack";
+}
+
+void TypePrinter::printSubstBuiltinTemplatePackAfter(
+ const SubstBuiltinTemplatePackType *T, raw_ostream &OS) {}
+
void TypePrinter::printSubstTemplateTypeParmPackBefore(
const SubstTemplateTypeParmPackType *T,
raw_ostream &OS) {
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 0277dfb5aac13..4b9f4326a49d4 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -13,7 +13,9 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticParse.h"
+#include "clang/Basic/DiagnosticSema.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/DeclSpec.h"
@@ -1305,6 +1307,17 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
}
}
+ // We do not allow to reference builtin templates that produce multiple
+ // values, they would not have a well-defined semantics outside template
+ // arguments.
+ if (auto *T = !Result.isInvalid()
+ ? Result.getAsTemplate().get().getAsTemplateDecl()
+ : nullptr;
+ T && isPackProducingBuiltinTemplate(T)) {
+ Actions.diagnoseMissingTemplateArguments(Result.getAsTemplate().get(),
+ Result.getLocation());
+ }
+
// If this is a pack expansion, build it as such.
if (EllipsisLoc.isValid() && !Result.isInvalid())
Result = Actions.ActOnPackExpansion(Result, EllipsisLoc);
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index da85959c005fe..5e028bce3d0cc 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -307,7 +307,7 @@ static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
UnsignedOrNone NumExpansions = FE->getNumExpansions();
if (S.CheckParameterPacksForExpansion(
FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
- Expand, RetainExpansion, NumExpansions) ||
+ true, Expand, RetainExpansion, NumExpansions) ||
!Expand || RetainExpansion)
return std::nullopt;
@@ -1696,11 +1696,13 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption(
Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks);
for (const UnexpandedParameterPack &APack : APacks) {
- std::pair<unsigned, unsigned> DepthAndIndex = getDepthAndIndex(APack);
- auto it = llvm::find_if(BPacks, [&](const UnexpandedParameterPack &BPack) {
- return getDepthAndIndex(BPack) == DepthAndIndex;
+ auto ADI = getDepthAndIndex(APack);
+ if (!ADI)
+ continue;
+ auto It = llvm::find_if(BPacks, [&](const UnexpandedParameterPack &BPack) {
+ return getDepthAndIndex(BPack) == ADI;
});
- if (it != BPacks.end())
+ if (It != BPacks.end())
return true;
}
return false;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index f5b4614576086..e53c906f651be 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17992,7 +17992,12 @@ DeclResult Sema::ActOnTemplatedFriendTag(
collectUnexpandedParameterPacks(QualifierLoc, Unexpanded);
unsigned FriendDeclDepth = TempParamLists.front()->getDepth();
for (UnexpandedParameterPack &U : Unexpanded) {
- if (getDepthAndIndex(U).first >= FriendDeclDepth) {
+ unsigned Depth;
+ if (auto DI = getDepthAndIndex(U))
+ Depth = DI->first;
+ else
+ continue;
+ if (Depth >= FriendDeclDepth) {
auto *ND = dyn_cast<NamedDecl *>(U.first);
if (!ND)
ND = cast<const TemplateTypeParmType *>(U.first)->getDecl();
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index b6b8932588909..928151c268ae4 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -17,8 +17,13 @@
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Mangle.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/AST/TypeOrdering.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticSema.h"
@@ -26,6 +31,7 @@
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/UnsignedOrNone.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
@@ -33,14 +39,22 @@
#include "clang/Sema/Overload.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
+#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaCUDA.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/raw_ostream.h"
#include <optional>
using namespace clang;
@@ -316,6 +330,13 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
}
}
+ if (isPackProducingBuiltinTemplateName(Template) &&
+ S->getTemplateParamParent() == nullptr) {
+ Diag(Name.getBeginLoc(), diag::err_builtin_pack_outside_template);
+ // Recover by returning the template, even though we would never be able to
+ // substitute it.
+ }
+
TemplateResult = TemplateTy::make(Template);
return TemplateKind;
}
@@ -3468,6 +3489,28 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
return Context.getHLSLInlineSpirvType(Opcode, Size, Alignment, Operands);
}
+ case BTK__builtin_dedup_pack: {
+ assert(Converted.size() == 1 && "__builtin_dedup_pack should be given "
+ "a parameter pack");
+ TemplateArgument Ts = Converted[0];
+ // Delay the computation until we can compute the final result. We choose
+ // not to remove the duplicates upfront before substitution to keep the code
+ // simple.
+ if (Ts.isDependent())
+ return QualType();
+ assert(Ts.getKind() == clang::TemplateArgument::Pack);
+ llvm::SmallVector<TemplateArgument> OutArgs;
+ llvm::SmallDenseSet<QualType> Seen;
+ // Synthesize a new template argument list, removing duplicates.
+ for (auto T : Ts.getPackAsArray()) {
+ assert(T.getKind() == clang::TemplateArgument::Type);
+ if (!Seen.insert(T.getAsType().getCanonicalType()).second)
+ continue;
+ OutArgs.push_back(T);
+ }
+ return Context.getSubstBuiltinTemplatePack(
+ TemplateArgument::CreatePackCopy(Context, OutArgs));
+ }
}
llvm_unreachable("unexpected BuiltinTemplateDecl!");
}
@@ -5857,6 +5900,29 @@ bool Sema::CheckTemplateArgumentList(
}
}
+ // Check for builtins producing template packs at this position, we do not
+ // support them yet.
+ if (const NonTypeTemplateParmDecl *NTTP =
+ dyn_cast<NonTypeTemplateParmDecl>(*Param);
+ NTTP && NTTP->isPackExpansion()) {
+ auto TL = NTTP->getTypeSourceInfo()
+ ->getTypeLoc()
+ .castAs<PackExpansionTypeLoc>();
+ llvm::SmallVector<UnexpandedParameterPack> Unexpanded;
+ collectUnexpandedParameterPacks(TL.getPatternLoc(), Unexpanded);
+ for (const auto &UPP : Unexpanded) {
+ auto *TST = UPP.first.dyn_cast<const TemplateSpecializationType *>();
+ if (!TST)
+ continue;
+ assert(isPackProducingBuiltinTemplateName(TST->getTemplateName()));
+ // It is not yet supported in many positions.
+ Diag(TL.getEllipsisLoc(),
+ diag::err_unsupported_builtin_template_pack_position)
+ << TST->getTemplateName();
+ return true;
+ }
+ }
+
if (ArgIdx < NumArgs) {
TemplateArgumentLoc &ArgLoc = NewArgs[ArgIdx];
bool NonPackParameter =
@@ -6314,6 +6380,11 @@ bool UnnamedLocalNoLinkageFinder::VisitSubstTemplateTypeParmPackType(
return false;
}
+bool UnnamedLocalNoLinkageFinder::VisitSubstBuiltinTemplatePackType(
+ const SubstBuiltinTemplatePackType *) {
+ return false;
+}
+
bool UnnamedLocalNoLinkageFinder::VisitTemplateSpecializationType(
const TemplateSpecializationType*) {
return false;
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 0d70321335467..413fb5eefdde3 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -702,6 +702,9 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
// If the parameter is an alias template, there is nothing to deduce.
if (const auto *TD = TNP.getAsTemplateDecl(); TD && TD->isTypeAlias())
return TemplateDeductionResult::Success;
+ // Pack-producing templates can only be matched after substitution.
+ if (isPackProducingBuiltinTemplateName(TNP))
+ return TemplateDeductionResult::Success;
// FIXME: To preserve sugar, the TST needs to carry sugared resolved
// arguments.
@@ -935,7 +938,11 @@ class PackDeductionScope {
S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) {
unsigned Depth, Index;
- std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]);
+ if (auto DI = getDepthAndIndex(Unexpanded[I])) {
+ std::tie(Depth, Index) = *DI;
+ } else {
+ continue;
+ }
if (Depth == Info.getDeducedDepth())
AddPack(Index);
}
@@ -943,7 +950,6 @@ class PackDeductionScope {
// Look for unexpanded packs in the pattern.
Collect(Pattern);
- assert(!Packs.empty() && "Pack expansion without unexpanded packs?");
unsigned NumNamedPacks = Packs.size();
@@ -1865,6 +1871,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
case Type::TemplateTypeParm:
case Type::SubstTemplateTypeParmPack:
+ case Type::SubstBuiltinTemplatePack:
llvm_unreachable("Type nodes handled above");
case Type::Auto:
@@ -6992,6 +6999,11 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
OnlyDeduced, Depth, Used);
break;
}
+ case Type::SubstBuiltinTemplatePack: {
+ MarkUsedTemplateParameters(Ctx, cast<SubstPackType>(T)->getArgumentPack(),
+ OnlyDeduced, Depth, Used);
+ break;
+ }
case Type::InjectedClassName:
T = cast<InjectedClassNameType>(T)->getInjectedSpecializationType();
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 0d96d18de2d85..d4d7131199e15 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -10,6 +10,7 @@
//===----------------------------------------------------------------------===/
#include "TreeTransform.h"
+#include "TypeLocBuilder.h"
#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
@@ -21,10 +22,13 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/PrettyDeclStackTrace.h"
+#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeVisitor.h"
+#include "clang/Basic/Builtins.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
@@ -36,6 +40,7 @@
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/TemplateInstCallback.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/TimeProfiler.h"
@@ -1454,6 +1459,7 @@ namespace {
bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
SourceRange PatternRange,
ArrayRef<UnexpandedParameterPack> Unexpanded,
+ bool FailOnPackProducingTemplates,
bool &ShouldExpand, bool &RetainExpansion,
UnsignedOrNone &NumExpansions) {
if (SemaRef.CurrentInstantiationScope &&
@@ -1467,8 +1473,9 @@ namespace {
}
return getSema().CheckParameterPacksForExpansion(
- EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
- RetainExpansion, NumExpansions);
+ EllipsisLoc, PatternRange, Unexpanded, TemplateArgs,
+ FailOnPackProducingTemplates, ShouldExpand, RetainExpansion,
+ NumExpansions);
}
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
@@ -1510,6 +1517,21 @@ namespace {
}
}
+ MultiLevelTemplateArgumentList ForgetSubstitution() {
+ MultiLevelTemplateArgumentList New;
+ New.addOuterRetainedLevels(this->TemplateArgs.getNumLevels());
+
+ MultiLevelTemplateArgumentList Old =
+ const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+ const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs) =
+ std::move(New);
+ return Old;
+ }
+ void RememberSubstitution(MultiLevelTemplateArgumentList Old) {
+ const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs) =
+ std::move(Old);
+ }
+
TemplateArgument
getTemplateArgumentPackPatternForRewrite(const TemplateArgument &TA) {
if (TA.getKind() != TemplateArgument::Pack)
@@ -1700,6 +1722,21 @@ namespace {
return inherited::TransformTemplateArgument(Input, Output, Uneval);
}
+ using TreeTransform::TransformTemplateSpecializationType;
+ QualType
+ TransformTemplateSpecializationType(TypeLocBuilder &TLB,
+ TemplateSpecializationTypeLoc TL) {
+ auto *T = TL.getTypePtr();
+ if (!getSema().ArgPackSubstIndex || !T->isSugared() ||
+ !isPackProducingBuiltinTemplateName(T->getTemplateName()))
+ return TreeTransform::TransformTemplateSpecializationType(TLB, TL);
+ // Look through sugar to get to the SubstBuiltinTemplatePackType that we
+ // need to substitute into.
+ QualType R = TransformType(T->desugar());
+ TLB.pushTrivial(getSema().getASTContext(), R, TL.getBeginLoc());
+ return R;
+ }
+
UnsignedOrNone ComputeSizeOfPackExprWithoutSubstitution(
ArrayRef<TemplateArgument> PackArgs) {
// Don't do this when rewriting template parameters for CTAD:
@@ -1749,6 +1786,9 @@ namespace {
TransformSubstTemplateTypeParmPackType(TypeLocBuilder &TLB,
SubstTemplateTypeParmPackTypeLoc TL,
bool SuppressObjCLifetime);
+ QualType
+ TransformSubstBuiltinTemplatePackType(TypeLocBuilder &TLB,
+ SubstBuiltinTemplatePackTypeLoc TL);
CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
@@ -1985,7 +2025,7 @@ bool TemplateInstantiator::maybeInstantiateFunctionParameterToScope(
ExpansionTL.getTypePtr()->getNumExpansions();
UnsignedOrNone NumExpansions = OrigNumExpansions;
if (TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(),
- Pattern.getSourceRange(), Unexpanded,
+ Pattern.getSourceRange(), Unexpanded, true,
ShouldExpand, RetainExpansion, NumExpansions))
return true;
@@ -2747,6 +2787,17 @@ QualType TemplateInstantiator::TransformSubstTemplateTypeParmPackType(
getPackIndex(Pack), Arg, TL.getNameLoc());
}
+QualType TemplateInstantiator::TransformSubstBuiltinTemplatePackType(
+ TypeLocBuilder &TLB, SubstBuiltinTemplatePackTypeLoc TL) {
+ if (!getSema().ArgPackSubstIndex)
+ return TreeTransform::TransformSubstBuiltinTemplatePackType(TLB, TL);
+ auto &Sema = getSema();
+ TemplateArgument Result = getPackSubstitutedTemplateArgument(
+ Sema, TL.getTypePtr()->getArgumentPack());
+ TLB.pushTrivial(Sema.getASTContext(), Result.getAsType(), TL.getBeginLoc());
+ return Result.getAsType();
+}
+
static concepts::Requirement::SubstitutionDiagnostic *
createSubstDiag(Sema &S, TemplateDeductionInfo &Info,
Sema::EntityPrinter Printer) {
@@ -3478,6 +3529,69 @@ bool Sema::SubstDefaultArgument(
return false;
}
+// See TreeTransform::PreparePackForExpansion for the relevant comment.
+// This function implements the same concept for base specifiers.
+static bool
+PreparePackForExpansion(Sema &S, const CXXBaseSpecifier &Base,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ TypeSourceInfo *&Out, UnexpandedInfo &Info) {
+ SourceRange BaseSourceRange = Base.getSourceRange();
+ SourceLocation BaseEllipsisLoc = Base.getEllipsisLoc();
+ Info.Ellipsis = Base.getEllipsisLoc();
+ auto ComputeInfo = [&S, &TemplateArgs, BaseSourceRange, BaseEllipsisLoc](
+ TypeSourceInfo *BaseTypeInfo,
+ bool IsLateExpansionAttempt, UnexpandedInfo &Info) {
+ // This is a pack expansion. See whether we should expand it now, or
+ // wait until later.
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ S.collectUnexpandedParameterPacks(BaseTypeInfo->getTypeLoc(), Unexpanded);
+ if (IsLateExpansionAttempt) {
+ // Request expansion only when there is an opportunity to expand a pack
+ // that required a substituion first.
+ bool SawPackTypes =
+ llvm::any_of(Unexpanded, [](UnexpandedParameterPack P) {
+ return P.first.dyn_cast<const SubstBuiltinTemplatePackType *>();
+ });
+ if (!SawPackTypes) {
+ Info.Expand = false;
+ return false;
+ }
+ }
+
+ Info.Expand = false;
+ Info.RetainExpansion = false;
+ Info.NumExpansions = std::nullopt;
+ return S.CheckParameterPacksForExpansion(
+ BaseEllipsisLoc, BaseSourceRange, Unexpanded, TemplateArgs, false,
+ Info.Expand, Info.RetainExpansion, Info.NumExpansions);
+ };
+
+ if (ComputeInfo(Base.getTypeSourceInfo(), false, Info))
+ return true;
+
+ if (Info.Expand) {
+ Out = Base.getTypeSourceInfo();
+ return false;
+ }
+
+ // The resulting base specifier will (still) be a pack expansion.
+ {
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, std::nullopt);
+ Out = S.SubstType(Base.getTypeSourceInfo(), TemplateArgs,
+ BaseSourceRange.getBegin(), DeclarationName());
+ }
+ if (!Out->getType()->containsUnexpandedParameterPack())
+ return false;
+
+ // Some packs will learn their length after substitution.
+ // We may need to request their expansion.
+ if (ComputeInfo(Out, /*IsLateExpansionAttempt=*/true, Info))
+ return true;
+ if (Info.Expand)
+ Info.ExpandUnderForgetSubstitions = true;
+ return false;
+}
+
bool
Sema::SubstBaseSpecifiers(CXXRecordDecl *Instantiation,
CXXRecordDecl *Pattern,
@@ -3495,47 +3609,37 @@ Sema::SubstBaseSpecifiers(CXXRecordDecl *Instantiation,
}
SourceLocation EllipsisLoc;
- TypeSourceInfo *BaseTypeLoc;
+ TypeSourceInfo *BaseTypeLoc = nullptr;
if (Base.isPackExpansion()) {
- // This is a pack expansion. See whether we should expand it now, or
- // wait until later.
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- collectUnexpandedParameterPacks(Base.getTypeSourceInfo()->getTypeLoc(),
- Unexpanded);
- bool ShouldExpand = false;
- bool RetainExpansion = false;
- UnsignedOrNone NumExpansions = std::nullopt;
- if (CheckParameterPacksForExpansion(Base.getEllipsisLoc(),
- Base.getSourceRange(),
- Unexpanded,
- TemplateArgs, ShouldExpand,
- RetainExpansion,
- NumExpansions)) {
+ UnexpandedInfo Info;
+ if (PreparePackForExpansion(*this, Base, TemplateArgs, BaseTypeLoc,
+ Info)) {
Invalid = true;
continue;
}
// If we should expand this pack expansion now, do so.
- if (ShouldExpand) {
- for (unsigned I = 0; I != *NumExpansions; ++I) {
+ MultiLevelTemplateArgumentList EmptyList;
+ const MultiLevelTemplateArgumentList *ArgsForSubst = &TemplateArgs;
+ if (Info.ExpandUnderForgetSubstitions)
+ ArgsForSubst = &EmptyList;
+
+ if (Info.Expand) {
+ for (unsigned I = 0; I != *Info.NumExpansions; ++I) {
Sema::ArgPackSubstIndexRAII SubstIndex(*this, I);
- TypeSourceInfo *BaseTypeLoc = SubstType(Base.getTypeSourceInfo(),
- TemplateArgs,
- Base.getSourceRange().getBegin(),
- DeclarationName());
- if (!BaseTypeLoc) {
+ TypeSourceInfo *Expanded =
+ SubstType(BaseTypeLoc, *ArgsForSubst,
+ Base.getSourceRange().getBegin(), DeclarationName());
+ if (!Expanded) {
Invalid = true;
continue;
}
- if (CXXBaseSpecifier *InstantiatedBase
- = CheckBaseSpecifier(Instantiation,
- Base.getSourceRange(),
- Base.isVirtual(),
- Base.getAccessSpecifierAsWritten(),
- BaseTypeLoc,
- SourceLocation()))
+ if (CXXBaseSpecifier *InstantiatedBase = CheckBaseSpecifier(
+ Instantiation, Base.getSourceRange(), Base.isVirtual(),
+ Base.getAccessSpecifierAsWritten(), Expanded,
+ SourceLocation()))
InstantiatedBases.push_back(InstantiatedBase);
else
Invalid = true;
@@ -3547,10 +3651,9 @@ Sema::SubstBaseSpecifiers(CXXRecordDecl *Instantiation,
// The resulting base specifier will (still) be a pack expansion.
EllipsisLoc = Base.getEllipsisLoc();
Sema::ArgPackSubstIndexRAII SubstIndex(*this, std::nullopt);
- BaseTypeLoc = SubstType(Base.getTypeSourceInfo(),
- TemplateArgs,
- Base.getSourceRange().getBegin(),
- DeclarationName());
+ BaseTypeLoc =
+ SubstType(BaseTypeLoc, *ArgsForSubst,
+ Base.getSourceRange().getBegin(), DeclarationName());
} else {
BaseTypeLoc = SubstType(Base.getTypeSourceInfo(),
TemplateArgs,
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 87ec4f75863fe..cddb7047330ec 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -133,7 +133,7 @@ static void instantiateDependentAlignedAttr(
// FIXME: Use the actual location of the ellipsis.
SourceLocation EllipsisLoc = Aligned->getLocation();
if (S.CheckParameterPacksForExpansion(EllipsisLoc, Aligned->getRange(),
- Unexpanded, TemplateArgs, Expand,
+ Unexpanded, TemplateArgs, true, Expand,
RetainExpansion, NumExpansions))
return;
@@ -1914,7 +1914,8 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
UnsignedOrNone NumExpansions = std::nullopt;
if (SemaRef.CheckParameterPacksForExpansion(
D->getEllipsisLoc(), D->getSourceRange(), Unexpanded,
- TemplateArgs, ShouldExpand, RetainExpansion, NumExpansions))
+ TemplateArgs, true, ShouldExpand, RetainExpansion,
+ NumExpansions))
return nullptr;
assert(!RetainExpansion &&
@@ -3474,10 +3475,11 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
->getEllipsisLoc(),
SourceRange(TC->getConceptNameLoc(),
- TC->hasExplicitTemplateArgs() ?
- TC->getTemplateArgsAsWritten()->getRAngleLoc() :
- TC->getConceptNameInfo().getEndLoc()),
- Unexpanded, TemplateArgs, Expand, RetainExpansion, NumExpanded))
+ TC->hasExplicitTemplateArgs()
+ ? TC->getTemplateArgsAsWritten()->getRAngleLoc()
+ : TC->getConceptNameInfo().getEndLoc()),
+ Unexpanded, TemplateArgs, true, Expand, RetainExpansion,
+ NumExpanded))
return nullptr;
}
}
@@ -3565,12 +3567,9 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl(
UnsignedOrNone OrigNumExpansions =
Expansion.getTypePtr()->getNumExpansions();
UnsignedOrNone NumExpansions = OrigNumExpansions;
- if (SemaRef.CheckParameterPacksForExpansion(Expansion.getEllipsisLoc(),
- Pattern.getSourceRange(),
- Unexpanded,
- TemplateArgs,
- Expand, RetainExpansion,
- NumExpansions))
+ if (SemaRef.CheckParameterPacksForExpansion(
+ Expansion.getEllipsisLoc(), Pattern.getSourceRange(), Unexpanded,
+ TemplateArgs, true, Expand, RetainExpansion, NumExpansions))
return nullptr;
if (Expand) {
@@ -3736,12 +3735,9 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl(
bool Expand = true;
bool RetainExpansion = false;
UnsignedOrNone NumExpansions = std::nullopt;
- if (SemaRef.CheckParameterPacksForExpansion(D->getLocation(),
- TempParams->getSourceRange(),
- Unexpanded,
- TemplateArgs,
- Expand, RetainExpansion,
- NumExpansions))
+ if (SemaRef.CheckParameterPacksForExpansion(
+ D->getLocation(), TempParams->getSourceRange(), Unexpanded,
+ TemplateArgs, true, Expand, RetainExpansion, NumExpansions))
return nullptr;
if (Expand) {
@@ -4014,8 +4010,8 @@ Decl *TemplateDeclInstantiator::instantiateUnresolvedUsingDecl(
bool RetainExpansion = false;
UnsignedOrNone NumExpansions = std::nullopt;
if (SemaRef.CheckParameterPacksForExpansion(
- D->getEllipsisLoc(), D->getSourceRange(), Unexpanded, TemplateArgs,
- Expand, RetainExpansion, NumExpansions))
+ D->getEllipsisLoc(), D->getSourceRange(), Unexpanded, TemplateArgs,
+ true, Expand, RetainExpansion, NumExpansions))
return nullptr;
// This declaration cannot appear within a function template signature,
@@ -6414,11 +6410,9 @@ Sema::InstantiateMemInitializers(CXXConstructorDecl *New,
bool RetainExpansion = false;
UnsignedOrNone NumExpansions = std::nullopt;
if (CheckParameterPacksForExpansion(Init->getEllipsisLoc(),
- BaseTL.getSourceRange(),
- Unexpanded,
- TemplateArgs, ShouldExpand,
- RetainExpansion,
- NumExpansions)) {
+ BaseTL.getSourceRange(), Unexpanded,
+ TemplateArgs, true, ShouldExpand,
+ RetainExpansion, NumExpansions)) {
AnyErrors = true;
New->setInvalidDecl();
continue;
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index d2baa2efb6121..3abe70d5d93a0 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -9,10 +9,15 @@
//===----------------------------------------------------------------------===/
#include "TypeLocBuilder.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DependenceFlags.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/TemplateBase.h"
+#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/ParsedTemplate.h"
@@ -20,7 +25,9 @@
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Template.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/SaveAndRestore.h"
+#include <memory>
#include <optional>
using namespace clang;
@@ -65,6 +72,41 @@ class CollectUnexpandedParameterPacksVisitor
Unexpanded.push_back({T, Loc});
}
+ bool addUnexpanded(const SubstBuiltinTemplatePackType *T,
+ SourceLocation Loc = SourceLocation()) {
+ Unexpanded.push_back({T, Loc});
+ return true;
+ }
+
+ bool addUnexpanded(const TemplateSpecializationType *T,
+ SourceLocation Loc = SourceLocation()) {
+ assert(T->isCanonicalUnqualified() &&
+ isPackProducingBuiltinTemplateName(T->getTemplateName()));
+ Unexpanded.push_back({T, Loc});
+ return true;
+ }
+
+ /// Returns true iff it handled the traversal. On false, the callers must
+ /// traverse themselves.
+ bool
+ TryTraverseSpecializationProducingPacks(const TemplateSpecializationType *T,
+ SourceLocation Loc) {
+ if (!isPackProducingBuiltinTemplateName(T->getTemplateName()))
+ return false;
+ // Canonical types are inputs to the initial substitution. Report them and
+ // do not recurse any further.
+ if (T->isCanonicalUnqualified()) {
+ addUnexpanded(T, Loc);
+ return true;
+ }
+ // For sugared types, do not use the default traversal as it would be
+ // looking at (now irrelevant) template arguments. Instead, look at the
+ // result of substitution, it usually contains SubstPackType that needs to
+ // be expanded further.
+ DynamicRecursiveASTVisitor::TraverseType(T->desugar());
+ return true;
+ }
+
public:
explicit CollectUnexpandedParameterPacksVisitor(
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded)
@@ -123,6 +165,22 @@ class CollectUnexpandedParameterPacksVisitor
return DynamicRecursiveASTVisitor::TraverseTemplateName(Template);
}
+ bool TraverseTemplateSpecializationTypeLoc(
+ TemplateSpecializationTypeLoc T) override {
+ if (TryTraverseSpecializationProducingPacks(T.getTypePtr(),
+ T.getBeginLoc()))
+ return true;
+ return DynamicRecursiveASTVisitor::TraverseTemplateSpecializationTypeLoc(
+ T);
+ }
+
+ bool
+ TraverseTemplateSpecializationType(TemplateSpecializationType *T) override {
+ if (TryTraverseSpecializationProducingPacks(T, SourceLocation()))
+ return true;
+ return DynamicRecursiveASTVisitor::TraverseTemplateSpecializationType(T);
+ }
+
/// Suppress traversal into Objective-C container literal
/// elements that are pack expansions.
bool TraverseObjCDictionaryLiteral(ObjCDictionaryLiteral *E) override {
@@ -320,6 +378,14 @@ class CollectUnexpandedParameterPacksVisitor
return DynamicRecursiveASTVisitor::TraverseUnresolvedLookupExpr(E);
}
+ bool TraverseSubstBuiltinTemplatePackType(
+ SubstBuiltinTemplatePackType *T) override {
+ addUnexpanded(T);
+ // Do not call into base implementation to supress traversal of the
+ // substituted types.
+ return true;
+ }
+
#ifndef NDEBUG
bool TraverseFunctionParmPackExpr(FunctionParmPackExpr *) override {
ContainsIntermediatePacks = true;
@@ -727,7 +793,7 @@ QualType Sema::CheckPackExpansion(QualType Pattern, SourceRange PatternRange,
if (!Pattern->containsUnexpandedParameterPack() &&
!Pattern->getContainedDeducedType()) {
Diag(EllipsisLoc, diag::err_pack_expansion_without_parameter_packs)
- << PatternRange;
+ << PatternRange;
return QualType();
}
@@ -761,7 +827,8 @@ ExprResult Sema::CheckPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc,
bool Sema::CheckParameterPacksForExpansion(
SourceLocation EllipsisLoc, SourceRange PatternRange,
ArrayRef<UnexpandedParameterPack> Unexpanded,
- const MultiLevelTemplateArgumentList &TemplateArgs, bool &ShouldExpand,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ bool FailOnPackProducingTemplates, bool &ShouldExpand,
bool &RetainExpansion, UnsignedOrNone &NumExpansions) {
ShouldExpand = true;
RetainExpansion = false;
@@ -777,12 +844,32 @@ bool Sema::CheckParameterPacksForExpansion(
IdentifierInfo *Name;
bool IsVarDeclPack = false;
FunctionParmPackExpr *BindingPack = nullptr;
+ std::optional<unsigned> NumPrecomputedArguments;
- if (const TemplateTypeParmType *TTP =
- ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) {
+ if (auto *TTP = ParmPack.first.dyn_cast<const TemplateTypeParmType *>()) {
Depth = TTP->getDepth();
Index = TTP->getIndex();
Name = TTP->getIdentifier();
+ } else if (auto *TST =
+ ParmPack.first
+ .dyn_cast<const TemplateSpecializationType *>()) {
+ assert(isPackProducingBuiltinTemplateName(TST->getTemplateName()));
+ // Delay expansion, substitution is required to know the size.
+ ShouldExpand = false;
+ if (!FailOnPackProducingTemplates)
+ continue;
+
+ // It is not yet supported in many positions.
+ Diag(PatternRange.getBegin().isValid() ? PatternRange.getBegin()
+ : EllipsisLoc,
+ diag::err_unsupported_builtin_template_pack_position)
+ << TST->getTemplateName();
+ return true;
+ } else if (auto *S =
+ ParmPack.first
+ .dyn_cast<const SubstBuiltinTemplatePackType *>()) {
+ Name = nullptr;
+ NumPrecomputedArguments = S->getNumArgs();
} else {
NamedDecl *ND = cast<NamedDecl *>(ParmPack.first);
if (isa<VarDecl>(ND))
@@ -822,6 +909,8 @@ bool Sema::CheckParameterPacksForExpansion(
}
} else if (BindingPack) {
NewPackSize = BindingPack->getNumExpansions();
+ } else if (NumPrecomputedArguments) {
+ NewPackSize = *NumPrecomputedArguments;
} else {
// If we don't have a template argument at this depth/index, then we
// cannot expand the pack expansion. Make a note of this, but we still
@@ -963,6 +1052,20 @@ UnsignedOrNone Sema::getNumArgumentsInExpansionFromUnexpanded(
Unexpanded[I].first.dyn_cast<const TemplateTypeParmType *>()) {
Depth = TTP->getDepth();
Index = TTP->getIndex();
+ } else if (auto *TST =
+ Unexpanded[I]
+ .first.dyn_cast<const TemplateSpecializationType *>()) {
+ // This is a dependent pack, we are not ready to expand it yet.
+ assert(isPackProducingBuiltinTemplateName(TST->getTemplateName()));
+ return std::nullopt;
+ } else if (auto *PST =
+ Unexpanded[I]
+ .first
+ .dyn_cast<const SubstBuiltinTemplatePackType *>()) {
+ assert((!Result || *Result == PST->getNumArgs()) &&
+ "inconsistent pack sizes");
+ Result = PST->getNumArgs();
+ continue;
} else {
NamedDecl *ND = cast<NamedDecl *>(Unexpanded[I].first);
if (isa<VarDecl>(ND)) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 5ff5fbf52ae25..7602284429d0d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -30,14 +30,20 @@
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
+#include "clang/AST/TemplateBase.h"
+#include "clang/AST/Type.h"
+#include "clang/AST/TypeLoc.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/OpenMPKinds.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/UnsignedOrNone.h"
#include "clang/Sema/Designator.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaObjC.h"
@@ -45,7 +51,9 @@
#include "clang/Sema/SemaOpenMP.h"
#include "clang/Sema/SemaPseudoObject.h"
#include "clang/Sema/SemaSYCL.h"
+#include "clang/Sema/Template.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>
#include <optional>
@@ -55,6 +63,17 @@ using namespace llvm::omp;
namespace clang {
using namespace sema;
+// This helper class is used to facilitate pack expansion during tree transform.
+struct UnexpandedInfo {
+ SourceLocation Ellipsis;
+ UnsignedOrNone OrigNumExpansions = std::nullopt;
+
+ bool Expand = false;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions = std::nullopt;
+ bool ExpandUnderForgetSubstitions = false;
+};
+
/// A semantic tree transformation that allows one to transform one
/// abstract syntax tree into another.
///
@@ -292,6 +311,7 @@ class TreeTransform {
bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
SourceRange PatternRange,
ArrayRef<UnexpandedParameterPack> Unexpanded,
+ bool FailOnPackProducingTemplates,
bool &ShouldExpand, bool &RetainExpansion,
UnsignedOrNone &NumExpansions) {
ShouldExpand = false;
@@ -314,6 +334,27 @@ class TreeTransform {
/// This routine is meant to be overridden by the template instantiator.
void RememberPartiallySubstitutedPack(TemplateArgument Arg) { }
+ /// "Forget" the template substitution to allow transforming the AST without
+ /// any template instantiations. This is used to expand template packs when
+ /// their size is not known in advance (e.g. for builtins that produce type
+ /// packs).
+ MultiLevelTemplateArgumentList ForgetSubstitution() { return {}; }
+ void RememberSubstitution(MultiLevelTemplateArgumentList) {}
+
+private:
+ struct ForgetSubstitutionRAII {
+ Derived &Self;
+ MultiLevelTemplateArgumentList Old;
+
+ public:
+ ForgetSubstitutionRAII(Derived &Self) : Self(Self) {
+ Old = Self.ForgetSubstitution();
+ }
+
+ ~ForgetSubstitutionRAII() { Self.RememberSubstitution(std::move(Old)); }
+ };
+
+public:
/// Note to the derived class when a function parameter pack is
/// being expanded.
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { }
@@ -660,6 +701,19 @@ class TreeTransform {
TemplateArgumentListInfo &Outputs,
bool Uneval = false);
+ /// Checks if the argument pack from \p In will need to be expanded and does
+ /// the necessary prework.
+ /// Whether the expansion is needed is captured in Info.Expand.
+ ///
+ /// - When the expansion is required, \p Out will be a template pattern that
+ /// would need to be expanded.
+ /// - When the expansion must not happen, \p Out will be a pack that must be
+ /// returned to the outputs directly.
+ ///
+ /// \return true iff the error occurred
+ bool PreparePackForExpansion(TemplateArgumentLoc In, bool Uneval,
+ TemplateArgumentLoc &Out, UnexpandedInfo &Info);
+
/// Fakes up a TemplateArgumentLoc for a given TemplateArgument.
void InventTemplateArgumentLoc(const TemplateArgument &Arg,
TemplateArgumentLoc &ArgLoc);
@@ -4477,11 +4531,9 @@ bool TreeTransform<Derived>::TransformExprs(Expr *const *Inputs,
bool RetainExpansion = false;
UnsignedOrNone OrigNumExpansions = Expansion->getNumExpansions();
UnsignedOrNone NumExpansions = OrigNumExpansions;
- if (getDerived().TryExpandParameterPacks(Expansion->getEllipsisLoc(),
- Pattern->getSourceRange(),
- Unexpanded,
- Expand, RetainExpansion,
- NumExpansions))
+ if (getDerived().TryExpandParameterPacks(
+ Expansion->getEllipsisLoc(), Pattern->getSourceRange(),
+ Unexpanded, true, Expand, RetainExpansion, NumExpansions))
return true;
if (!Expand) {
@@ -5063,60 +5115,30 @@ 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))
+ UnexpandedInfo Info;
+ TemplateArgumentLoc Prepared;
+ if (PreparePackForExpansion(In, Uneval, Prepared, Info))
return true;
-
- 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;
-
- Out = getDerived().RebuildPackExpansion(OutPattern, Ellipsis,
- NumExpansions);
- if (Out.getArgument().isNull())
- return true;
-
- Outputs.addArgument(Out);
+ if (!Info.Expand) {
+ Outputs.addArgument(Prepared);
continue;
}
// The transform has determined that we should perform an elementwise
// expansion of the pattern. Do so.
- for (unsigned I = 0; I != *NumExpansions; ++I) {
+ std::optional<ForgetSubstitutionRAII> ForgeSubst;
+ if (Info.ExpandUnderForgetSubstitions)
+ ForgeSubst.emplace(getDerived());
+ for (unsigned I = 0; I != *Info.NumExpansions; ++I) {
Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), I);
- if (getDerived().TransformTemplateArgument(Pattern, Out, Uneval))
+ TemplateArgumentLoc Out;
+ if (getDerived().TransformTemplateArgument(Prepared, Out, Uneval))
return true;
if (Out.getArgument().containsUnexpandedParameterPack()) {
- Out = getDerived().RebuildPackExpansion(Out, Ellipsis,
- OrigNumExpansions);
+ Out = getDerived().RebuildPackExpansion(Out, Info.Ellipsis,
+ Info.OrigNumExpansions);
if (Out.getArgument().isNull())
return true;
}
@@ -5126,14 +5148,15 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
// If we're supposed to retain a pack expansion, do so by temporarily
// forgetting the partially-substituted parameter pack.
- if (RetainExpansion) {
+ if (Info.RetainExpansion) {
ForgetPartiallySubstitutedPackRAII Forget(getDerived());
- if (getDerived().TransformTemplateArgument(Pattern, Out, Uneval))
+ TemplateArgumentLoc Out;
+ if (getDerived().TransformTemplateArgument(Prepared, Out, Uneval))
return true;
- Out = getDerived().RebuildPackExpansion(Out, Ellipsis,
- OrigNumExpansions);
+ Out = getDerived().RebuildPackExpansion(Out, Info.Ellipsis,
+ Info.OrigNumExpansions);
if (Out.getArgument().isNull())
return true;
@@ -5154,6 +5177,84 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
}
+template <typename Derived>
+bool TreeTransform<Derived>::PreparePackForExpansion(TemplateArgumentLoc In,
+ bool Uneval,
+ TemplateArgumentLoc &Out,
+ UnexpandedInfo &Info) {
+ auto ComputeInfo = [this](TemplateArgumentLoc Arg,
+ bool IsLateExpansionAttempt, UnexpandedInfo &Info,
+ TemplateArgumentLoc &Pattern) {
+ assert(Arg.getArgument().isPackExpansion());
+ // We have a pack expansion, for which we will be substituting into the
+ // pattern.
+ Pattern = getSema().getTemplateArgumentPackExpansionPattern(
+ Arg, Info.Ellipsis, Info.OrigNumExpansions);
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ getSema().collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ if (IsLateExpansionAttempt) {
+ // Request expansion only when there is an opportunity to expand a pack
+ // that required a substituion first.
+ bool SawPackTypes =
+ llvm::any_of(Unexpanded, [](UnexpandedParameterPack P) {
+ return P.first.dyn_cast<const SubstBuiltinTemplatePackType *>();
+ });
+ if (!SawPackTypes) {
+ Info.Expand = false;
+ return false;
+ }
+ }
+ assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+
+ // Determine whether the set of unexpanded parameter packs can and
+ // should be expanded.
+ Info.Expand = true;
+ Info.RetainExpansion = false;
+ Info.NumExpansions = Info.OrigNumExpansions;
+ return getDerived().TryExpandParameterPacks(
+ Info.Ellipsis, Pattern.getSourceRange(), Unexpanded, false, Info.Expand,
+ Info.RetainExpansion, Info.NumExpansions);
+ };
+
+ TemplateArgumentLoc Pattern;
+ if (ComputeInfo(In, false, Info, Pattern))
+ return true;
+
+ if (Info.Expand) {
+ Out = Pattern;
+ 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;
+
+ Out = getDerived().RebuildPackExpansion(OutPattern, Info.Ellipsis,
+ Info.NumExpansions);
+ if (Out.getArgument().isNull())
+ return true;
+ SubstIndex.reset();
+
+ if (!OutPattern.getArgument().containsUnexpandedParameterPack())
+ return false;
+
+ // Some packs will learn their length after substitution.
+ // We may need to request their expansion.
+ ForgetSubstitutionRAII ForgetSubst(getDerived());
+ if (ComputeInfo(Out, true, Info, OutPattern))
+ return true;
+ if (!Info.Expand)
+ return false;
+ Out = OutPattern;
+ Info.ExpandUnderForgetSubstitions = true;
+ return false;
+}
+
//===----------------------------------------------------------------------===//
// Type transformation
//===----------------------------------------------------------------------===//
@@ -6211,12 +6312,10 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams(
if (Unexpanded.size() > 0) {
OrigNumExpansions = ExpansionTL.getTypePtr()->getNumExpansions();
NumExpansions = OrigNumExpansions;
- if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(),
- Pattern.getSourceRange(),
- Unexpanded,
- ShouldExpand,
- RetainExpansion,
- NumExpansions)) {
+ if (getDerived().TryExpandParameterPacks(
+ ExpansionTL.getEllipsisLoc(), Pattern.getSourceRange(),
+ Unexpanded, true, ShouldExpand, RetainExpansion,
+ NumExpansions)) {
return true;
}
} else {
@@ -6322,11 +6421,9 @@ bool TreeTransform<Derived>::TransformFunctionTypeParams(
// Determine whether we should expand the parameter packs.
bool ShouldExpand = false;
bool RetainExpansion = false;
- if (getDerived().TryExpandParameterPacks(Loc, SourceRange(),
- Unexpanded,
- ShouldExpand,
- RetainExpansion,
- NumExpansions)) {
+ if (getDerived().TryExpandParameterPacks(
+ Loc, SourceRange(), Unexpanded, true, ShouldExpand,
+ RetainExpansion, NumExpansions)) {
return true;
}
@@ -6622,9 +6719,9 @@ bool TreeTransform<Derived>::TransformExceptionSpec(
UnsignedOrNone NumExpansions = PackExpansion->getNumExpansions();
// FIXME: Track the location of the ellipsis (and track source location
// information for the types in the exception specification in general).
- if (getDerived().TryExpandParameterPacks(
- Loc, SourceRange(), Unexpanded, Expand,
- RetainExpansion, NumExpansions))
+ if (getDerived().TryExpandParameterPacks(Loc, SourceRange(), Unexpanded,
+ true, Expand, RetainExpansion,
+ NumExpansions))
return true;
if (!Expand) {
@@ -6899,7 +6996,7 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
bool RetainExpansion = false;
UnsignedOrNone NumExpansions = std::nullopt;
if (getDerived().TryExpandParameterPacks(TL.getEllipsisLoc(), SourceRange(),
- Unexpanded, ShouldExpand,
+ Unexpanded, true, ShouldExpand,
RetainExpansion, NumExpansions))
return QualType();
if (!ShouldExpand) {
@@ -7126,6 +7223,11 @@ QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmType(
return Result;
}
+template <typename Derived>
+QualType TreeTransform<Derived>::TransformSubstBuiltinTemplatePackType(
+ TypeLocBuilder &TLB, SubstBuiltinTemplatePackTypeLoc TL) {
+ return TransformTypeSpecType(TLB, TL);
+}
template<typename Derived>
QualType TreeTransform<Derived>::TransformSubstTemplateTypeParmPackType(
@@ -7981,8 +8083,8 @@ TreeTransform<Derived>::TransformObjCObjectType(TypeLocBuilder &TLB,
bool RetainExpansion = false;
UnsignedOrNone NumExpansions = PackExpansion->getNumExpansions();
if (getDerived().TryExpandParameterPacks(
- PackExpansionLoc.getEllipsisLoc(), PatternLoc.getSourceRange(),
- Unexpanded, Expand, RetainExpansion, NumExpansions))
+ PackExpansionLoc.getEllipsisLoc(), PatternLoc.getSourceRange(),
+ Unexpanded, true, Expand, RetainExpansion, NumExpansions))
return QualType();
if (!Expand) {
@@ -14916,11 +15018,9 @@ TreeTransform<Derived>::TransformTypeTraitExpr(TypeTraitExpr *E) {
UnsignedOrNone OrigNumExpansions =
ExpansionTL.getTypePtr()->getNumExpansions();
UnsignedOrNone NumExpansions = OrigNumExpansions;
- if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(),
- PatternTL.getSourceRange(),
- Unexpanded,
- Expand, RetainExpansion,
- NumExpansions))
+ if (getDerived().TryExpandParameterPacks(
+ ExpansionTL.getEllipsisLoc(), PatternTL.getSourceRange(),
+ Unexpanded, true, Expand, RetainExpansion, NumExpansions))
return ExprError();
if (!Expand) {
@@ -15494,9 +15594,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
ExpansionTL.getTypePtr()->getNumExpansions();
UnsignedOrNone NumExpansions = OrigNumExpansions;
if (getDerived().TryExpandParameterPacks(
- ExpansionTL.getEllipsisLoc(),
- OldVD->getInit()->getSourceRange(), Unexpanded, Expand,
- RetainExpansion, NumExpansions))
+ ExpansionTL.getEllipsisLoc(), OldVD->getInit()->getSourceRange(),
+ Unexpanded, true, Expand, RetainExpansion, NumExpansions))
return ExprError();
assert(!RetainExpansion && "Should not need to retain expansion after a "
"capture since it cannot be extended");
@@ -15655,11 +15754,9 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
bool ShouldExpand = false;
bool RetainExpansion = false;
UnsignedOrNone NumExpansions = std::nullopt;
- if (getDerived().TryExpandParameterPacks(C->getEllipsisLoc(),
- C->getLocation(),
- Unexpanded,
- ShouldExpand, RetainExpansion,
- NumExpansions)) {
+ if (getDerived().TryExpandParameterPacks(
+ C->getEllipsisLoc(), C->getLocation(), Unexpanded, true,
+ ShouldExpand, RetainExpansion, NumExpansions)) {
Invalid = true;
continue;
}
@@ -16180,10 +16277,9 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
bool ShouldExpand = false;
bool RetainExpansion = false;
UnsignedOrNone NumExpansions = std::nullopt;
- if (getDerived().TryExpandParameterPacks(E->getOperatorLoc(), E->getPackLoc(),
- Unexpanded,
- ShouldExpand, RetainExpansion,
- NumExpansions))
+ if (getDerived().TryExpandParameterPacks(
+ E->getOperatorLoc(), E->getPackLoc(), Unexpanded, true,
+ ShouldExpand, RetainExpansion, NumExpansions))
return ExprError();
// If we need to expand the pack, build a template argument from it and
@@ -16299,7 +16395,7 @@ TreeTransform<Derived>::TransformPackIndexingExpr(PackIndexingExpr *E) {
UnsignedOrNone OrigNumExpansions = std::nullopt,
NumExpansions = std::nullopt;
if (getDerived().TryExpandParameterPacks(
- E->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded,
+ E->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, true,
ShouldExpand, RetainExpansion, NumExpansions))
return true;
if (!ShouldExpand) {
@@ -16406,11 +16502,9 @@ TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
bool RetainExpansion = false;
UnsignedOrNone OrigNumExpansions = E->getNumExpansions(),
NumExpansions = OrigNumExpansions;
- if (getDerived().TryExpandParameterPacks(E->getEllipsisLoc(),
- Pattern->getSourceRange(),
- Unexpanded,
- Expand, RetainExpansion,
- NumExpansions))
+ if (getDerived().TryExpandParameterPacks(
+ E->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, true,
+ Expand, RetainExpansion, NumExpansions))
return true;
if (!Expand) {
@@ -16644,9 +16738,9 @@ TreeTransform<Derived>::TransformObjCDictionaryLiteral(
UnsignedOrNone NumExpansions = OrigNumExpansions;
SourceRange PatternRange(OrigElement.Key->getBeginLoc(),
OrigElement.Value->getEndLoc());
- if (getDerived().TryExpandParameterPacks(OrigElement.EllipsisLoc,
- PatternRange, Unexpanded, Expand,
- RetainExpansion, NumExpansions))
+ if (getDerived().TryExpandParameterPacks(
+ OrigElement.EllipsisLoc, PatternRange, Unexpanded, true, Expand,
+ RetainExpansion, NumExpansions))
return ExprError();
if (!Expand) {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 95e92cf6790d7..d9cdbacc48ac7 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7480,6 +7480,11 @@ void TypeLocReader::VisitSubstTemplateTypeParmPackTypeLoc(
TL.setNameLoc(readSourceLocation());
}
+void TypeLocReader::VisitSubstBuiltinTemplatePackTypeLoc(
+ SubstBuiltinTemplatePackTypeLoc TL) {
+ TL.setNameLoc(readSourceLocation());
+}
+
void TypeLocReader::VisitTemplateSpecializationTypeLoc(
TemplateSpecializationTypeLoc TL) {
TL.setTemplateKeywordLoc(readSourceLocation());
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 955f7b914f5db..de0f258811e7b 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -610,6 +610,11 @@ void TypeLocWriter::VisitSubstTemplateTypeParmPackTypeLoc(
addSourceLocation(TL.getNameLoc());
}
+void TypeLocWriter::VisitSubstBuiltinTemplatePackTypeLoc(
+ SubstBuiltinTemplatePackTypeLoc TL) {
+ addSourceLocation(TL.getNameLoc());
+}
+
void TypeLocWriter::VisitTemplateSpecializationTypeLoc(
TemplateSpecializationTypeLoc TL) {
addSourceLocation(TL.getTemplateKeywordLoc());
@@ -1053,6 +1058,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(TYPE_PACK_EXPANSION);
RECORD(TYPE_ATTRIBUTED);
RECORD(TYPE_SUBST_TEMPLATE_TYPE_PARM_PACK);
+ RECORD(TYPE_SUBST_BUILTIN_TEMPLATE_PACK);
RECORD(TYPE_AUTO);
RECORD(TYPE_UNARY_TRANSFORM);
RECORD(TYPE_ATOMIC);
diff --git a/clang/test/Import/builtin-template/Inputs/S.cpp b/clang/test/Import/builtin-template/Inputs/S.cpp
index d5c9a9ae0309d..8068865b1c6a1 100644
--- a/clang/test/Import/builtin-template/Inputs/S.cpp
+++ b/clang/test/Import/builtin-template/Inputs/S.cpp
@@ -14,3 +14,10 @@ using TypePackElement = __type_pack_element<i, T...>;
template <int i>
struct X;
+
+
+template <template <class...> class Templ, class...Types>
+using TypePackDedup = Templ<__builtin_dedup_pack<Types...>...>;
+
+template <class ...Ts>
+struct TypeList {};
diff --git a/clang/test/Import/builtin-template/test.cpp b/clang/test/Import/builtin-template/test.cpp
index 590efad0c71dc..bc5e76e718ef1 100644
--- a/clang/test/Import/builtin-template/test.cpp
+++ b/clang/test/Import/builtin-template/test.cpp
@@ -1,9 +1,11 @@
// RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DSEQ | FileCheck --check-prefix=CHECK-SEQ %s
// RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DPACK | FileCheck --check-prefix=CHECK-PACK %s
-// RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DPACK -Xcc -DSEQ | FileCheck --check-prefixes=CHECK-SEQ,CHECK-PACK %s
+// RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DDEDUP | FileCheck --check-prefix=CHECK-DEDUP %s
+// RUN: clang-import-test -dump-ast -import %S/Inputs/S.cpp -expression %s -Xcc -DPACK -Xcc -DSEQ -Xcc -DDEDUP | FileCheck --check-prefixes=CHECK-SEQ,CHECK-PACK,CHECK-DEDUP %s
// CHECK-SEQ: BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __make_integer_seq{{$}}
// CHECK-PACK: BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __type_pack_element{{$}}
+// CHECK-DEDUP: BuiltinTemplateDecl {{.+}} <<invalid sloc>> <invalid sloc> implicit __builtin_dedup_pack{{$}}
void expr() {
#ifdef SEQ
@@ -20,4 +22,10 @@ void expr() {
static_assert(__is_same(TypePackElement<0, X<0>, X<1>>, X<0>), "");
static_assert(__is_same(TypePackElement<1, X<0>, X<1>>, X<1>), "");
#endif
+
+#ifdef DEDUP
+ static_assert(__is_same(TypePackDedup<TypeList>, TypeList<>), "");
+ static_assert(__is_same(TypePackDedup<TypeList, int, double, int>, TypeList<int, double>), "");
+ static_assert(__is_same(TypePackDedup<TypeList, X<0>, X<1>, X<1>, X<2>, X<0>>, TypeList<X<0>, X<1>, X<2>>), "");
+#endif
}
diff --git a/clang/test/PCH/dedup_types.cpp b/clang/test/PCH/dedup_types.cpp
new file mode 100644
index 0000000000000..d4b19b4411169
--- /dev/null
+++ b/clang/test/PCH/dedup_types.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++14 -x c++-header %s -emit-pch -o %t.pch
+// RUN: %clang_cc1 -std=c++14 -x c++ /dev/null -include-pch %t.pch
+
+// RUN: %clang_cc1 -std=c++14 -x c++-header %s -emit-pch -fpch-instantiate-templates -o %t.pch
+// RUN: %clang_cc1 -std=c++14 -x c++ /dev/null -include-pch %t.pch
+
+template <template <class...> class Templ, class...Types>
+using TypePackDedup = Templ<__builtin_dedup_pack<Types...>...>;
+
+template <class ...Ts>
+struct TypeList {};
+
+template <int i>
+struct X {};
+
+void fn1() {
+ TypeList<int, double> l1 = TypePackDedup<TypeList, int, double, int>{};
+ TypeList<> l2 = TypePackDedup<TypeList>{};
+ TypeList<X<0>, X<1>> x1 = TypePackDedup<TypeList, X<0>, X<1>, X<0>, X<1>>{};
+}
diff --git a/clang/test/SemaCXX/pr100095.cpp b/clang/test/SemaCXX/pr100095.cpp
index 15913fec9d5ae..9b8c09cb82977 100644
--- a/clang/test/SemaCXX/pr100095.cpp
+++ b/clang/test/SemaCXX/pr100095.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s
-// XFAIL: asserts
template <class> struct Pair;
template <class...> struct Tuple {
diff --git a/clang/test/SemaTemplate/dedup-types-builtin.cpp b/clang/test/SemaTemplate/dedup-types-builtin.cpp
new file mode 100644
index 0000000000000..374c76bad01d9
--- /dev/null
+++ b/clang/test/SemaTemplate/dedup-types-builtin.cpp
@@ -0,0 +1,226 @@
+// RUN: %clang_cc1 %s -verify
+template <typename...> struct TypeList;
+
+// === Check results of the builtin.
+template <class>
+struct TemplateWrapper {
+ static_assert(__is_same( // expected-error {{static assertion contains an unexpanded parameter pack}}
+ TypeList<__builtin_dedup_pack<int, int*, int, double, float>>,
+ TypeList<int, int*, double, float>));
+};
+
+template <template<typename ...> typename Templ, typename ...Types>
+struct Dependent {
+ using empty_list = Templ<__builtin_dedup_pack<>...>;
+ using same = Templ<__builtin_dedup_pack<Types...>...>;
+ using twice = Templ<__builtin_dedup_pack<Types..., Types...>...>;
+ using dep_only_types = TypeList<__builtin_dedup_pack<Types...>...>;
+ using dep_only_template = Templ<__builtin_dedup_pack<int, double, int>...>;
+};
+
+// Check the reverse condition to make sure we see an error and not accidentally produced dependent expression.
+static_assert(!__is_same(Dependent<TypeList>::empty_list, TypeList<>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList>::same, TypeList<>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList>::twice, TypeList<>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList>::dep_only_types, TypeList<>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList>::dep_only_template, TypeList<int, double>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, int*>::empty_list, TypeList<>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, int*>::same, TypeList<int*, double*>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, int*>::twice, TypeList<int*, double*>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, int*>::dep_only_types, TypeList<int*, double*>)); // expected-error {{static assertion failed}}
+static_assert(!__is_same(Dependent<TypeList, int*, double*, int*>::dep_only_template, TypeList<int, double>)); // expected-error {{static assertion failed}}
+
+
+template <class ...T>
+using Twice = TypeList<T..., T...>;
+
+template <class>
+struct TwiceTemplateWrapper {
+ static_assert(!__is_same(Twice<__builtin_dedup_pack<int, double, int>...>, TypeList<int, double, int, double>)); // expected-error {{static assertion failed}}
+
+};
+template struct TwiceTemplateWrapper<int>; // expected-note {{in instantiation of template class 'TwiceTemplateWrapper<int>' requested here}}
+
+template <int...> struct IntList;
+// Wrong kinds of template arguments.
+template <class> struct IntListTemplateWrapper {
+ IntList<__builtin_dedup_pack<int>...>* wrong_template; // expected-error {{template argument for non-type template parameter must be an expression}}
+ // expected-note at -4 {{template parameter is declared here}}
+ TypeList<__builtin_dedup_pack<1, 2, 3>...>* wrong_template_args; // expected-error {{template argument for template type parameter must be a type}}
+ // expected-note@* {{template parameter from hidden source}}
+ __builtin_dedup_pack<> not_enough_args; // expected-error {{data member type contains an unexpanded parameter pack}}
+ // expected-note@* {{template declaration from hidden source}}
+ __builtin_dedup_pack missing_template_args; // expected-error {{use of template '__builtin_dedup_pack' requires template arguments}}
+};
+
+// Make sure various canonical / non-canonical type representations do not affect results
+// of the deduplication and the qualifiers do end up creating different types when C++ requires it.
+using Int = int;
+using CInt = const Int;
+using IntArray = Int[10];
+using CIntArray = Int[10];
+using IntPtr = int*;
+using CIntPtr = const int*;
+
+template <class>
+struct Foo {
+ static_assert(
+ !__is_same( // expected-error {{static assertion failed}}
+ // expected-note@* {{in instantiation of template class 'Foo<int>'}}
+ TypeList<__builtin_dedup_pack<
+ Int, int,
+ const int, const Int, CInt, const CInt,
+ IntArray, Int[10], int[10],
+ const IntArray, const int[10], CIntArray, const CIntArray,
+ IntPtr, int*,
+ const IntPtr, int* const,
+ CIntPtr, const int*,
+ const IntPtr*, int*const*,
+ CIntPtr*, const int**,
+ const CIntPtr*, const int* const*
+ >...>,
+ TypeList<int, const int, int[10], const int [10], int*, int* const, const int*, int*const *, const int**, const int*const*>),
+ "");
+};
+
+template struct Foo<int>;
+
+// === Show an error when packs are used in non-template contexts.
+static_assert(!__is_same(TypeList<__builtin_dedup_pack<int>...>, TypeList<int>)); // expected-error {{outside}}
+// Non-dependent uses in template are fine, though.
+template <class T>
+struct NonDepInTemplate {
+ static_assert(!__is_same(TypeList<__builtin_dedup_pack<int>...>, TypeList<int>)); // expected-error {{static assertion failed}}
+};
+template struct NonDepInTemplate<int>; // expected-note {{requested here}}
+
+template <template<class...> class T = __builtin_dedup_pack> // expected-error {{use of template '__builtin_dedup_pack' requires template arguments}}
+ // expected-note@* {{template declaration from hidden source}}
+struct UseAsTemplate;
+template <template<class...> class>
+struct AcceptsTemplateArg;
+template <class>
+struct UseAsTemplateWrapper {
+ AcceptsTemplateArg<__builtin_dedup_pack>* a; // expected-error {{use of template '__builtin_dedup_pack' requires template arguments}}
+ // expected-note@* {{template declaration from hidden source}}
+};
+
+// === Check how expansions in various positions of the AST behave.
+// The following cases are not supported yet, should produce an error.
+template <class... T>
+struct DedupBases : __builtin_dedup_pack<T...>... {};
+struct Base1 {
+ int a1;
+};
+struct Base2 {
+ int a2;
+};
+static_assert(DedupBases<Base1, Base1, Base2, Base1, Base2, Base2>{1, 2}.a1 != 1); // expected-error {{static assertion failed}} \
+ // expected-note {{}}
+static_assert(DedupBases<Base1, Base1, Base2, Base1, Base2, Base2>{1, 2}.a2 != 2); // expected-error {{static assertion failed}} \
+ // expected-note {{}}
+
+template <class ...T>
+constexpr int dedup_params(__builtin_dedup_pack<T...>... as) {
+ return (as + ...);
+}
+static_assert(dedup_params<int, int, short, int, short, short>(1, 2)); // expected-error {{no matching function}} \
+ // expected-note at -3 {{expansions of '__builtin_dedup_pack' are not supported here}}
+
+template <class ...T>
+constexpr int dedup_params_into_type_list(TypeList<__builtin_dedup_pack<T...>...> *, T... as) {
+ return (as + ...);
+}
+static_assert(dedup_params_into_type_list(static_cast<TypeList<int,short,long>*>(nullptr), 1, short(1), 1, 1l, 1l) != 5); // expected-error {{static assertion failed}} \
+ // expected-note {{expression evaluates}}
+
+template <class T, __builtin_dedup_pack<T, int>...> // expected-error 2 {{expansions of '__builtin_dedup_pack' are not supported here}}
+struct InTemplateParams {};
+InTemplateParams<int> itp1;
+InTemplateParams<int, 1, 2, 3, 4, 5> itp2;
+
+template <class T>
+struct DeepTemplateParams {
+ template <__builtin_dedup_pack<T, int>...> // expected-error {{expansions of '__builtin_dedup_pack' are not supported here}}
+ struct Templ {};
+};
+DeepTemplateParams<int>::Templ<> dtp1; // expected-note {{requested here}} \
+ // expected-error {{no template named 'Templ'}}
+
+
+template <class ...T>
+struct MemInitializers : T... {
+ MemInitializers() : __builtin_dedup_pack<T...>()... {} // expected-error 2 {{expansions of '__builtin_dedup_pack' are not supported here.}}
+};
+MemInitializers<> mi1; // expected-note {{in instantiation of member function}}
+MemInitializers<Base1, Base2> mi2; // expected-note {{in instantiation of member function}}
+
+template <class ...T>
+constexpr int dedup_in_expressions() {
+ // counts the number of unique Ts.
+ return ((1 + __builtin_dedup_pack<T...>()) + ...); // expected-error {{expansions of '__builtin_dedup_pack' are not supported here.}} \
+ // expected-note at +3 {{in instantiation of function template specialization}}
+}
+static_assert(dedup_in_expressions<int, int, short, double, int, short, double, int>() == 3); // expected-error {{not an integral constant expression}}
+
+template <class ...T>
+void in_exception_spec() throw(__builtin_dedup_pack<T...>...); // expected-error{{C++17 does not allow dynamic exception specifications}} \
+ // expected-note {{use 'noexcept}} \
+ // expected-error{{expansions of '__builtin_dedup_pack' are not supported here.}}
+
+void test_in_exception_spec() {
+ in_exception_spec<int, double, int>(); // expected-note {{instantiation of exception specification}}
+}
+
+template <class ...T>
+constexpr bool in_type_trait = __is_trivially_constructible(int, __builtin_dedup_pack<T...>...); // expected-error{{expansions of '__builtin_dedup_pack' are not supported here.}}
+
+static_assert(in_type_trait<int, int, int>); // expected-note{{in instantiation of variable template specialization}}
+
+template <class ...T>
+struct InFriends {
+ friend __builtin_dedup_pack<T>...; // expected-warning {{variadic 'friend' declarations are a C++2c extension}} \
+ // expected-error 2 {{expansions of '__builtin_dedup_pack' are not supported here.}} \
+ // expected-note@* 2 {{in instantiation of template class}}
+
+};
+struct Friend1 {};
+struct Friend2 {};
+InFriends<> if1;
+InFriends<Friend1, Friend2> if2;
+
+template <class ...T>
+struct InUsingDecl {
+ using __builtin_dedup_pack<T...>::func...; // expected-error 2 {{expansions of '__builtin_dedup_pack' are not supported here.}}
+};
+struct WithFunc1 { void func(); };
+struct WithFunc2 { void func(int); };
+InUsingDecl<> iu1; // expected-note {{in instantiation of template class}}
+InUsingDecl<WithFunc1, WithFunc2> iu2; // expected-note {{in instantiation of template class}}
+
+// Note: produces parsing errors and does not construct pack indexing.
+// Keep this commented out until the parser supports this.
+//
+// template <class ...T>
+// struct InPackIndexing {
+//
+// using type = __builtin_dedup_pack<T...>...[0];
+// };
+// static_assert(__is_same(InPackIndexing<int, int>, int));
+
+template <class ...T>
+struct LambdaInitCaptures {
+ static constexpr int test() {
+ [...foos=__builtin_dedup_pack<T...>()]{}; // expected-warning {{initialized lambda pack captures are a C++20 extension}} \
+ // expected-error 2 {{expansions of '__builtin_dedup_pack' are not supported here.}}
+ return 3;
+ }
+};
+static_assert(LambdaInitCaptures<>::test() == 3); // expected-note {{in instantiation of member function}}
+static_assert(LambdaInitCaptures<int, int, int>::test() == 3); // expected-note {{in instantiation of member function}}
+
+template <class ...T>
+struct alignas(__builtin_dedup_pack<T...>...) AlignAs {}; // expected-error 2 {{expansions of '__builtin_dedup_pack' are not supported here.}}
+AlignAs<> aa1; // expected-note {{in instantiation of template class}}
+AlignAs<int, double> aa2; // expected-note {{in instantiation of template class}}
+
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 9c2724f17cf8b..496e12eb2021b 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1943,6 +1943,7 @@ DEFAULT_TYPELOC_IMPL(Record, TagType)
DEFAULT_TYPELOC_IMPL(Enum, TagType)
DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParm, Type)
DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParmPack, Type)
+DEFAULT_TYPELOC_IMPL(SubstBuiltinTemplatePack, Type)
DEFAULT_TYPELOC_IMPL(Auto, Type)
DEFAULT_TYPELOC_IMPL(BitInt, Type)
DEFAULT_TYPELOC_IMPL(DependentBitInt, Type)
>From 241cbf4a8577fd423df81402c0bd6f82817fcd62 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov at google.com>
Date: Tue, 5 Aug 2025 13:00:41 +0200
Subject: [PATCH 2/2] Use wider int type to avoid truncating builting
---
clang/include/clang/AST/Type.h | 25 ++++++++-----------------
clang/lib/AST/Type.cpp | 2 +-
2 files changed, 9 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 692cacb1061cd..a4b12fa2397ca 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2188,6 +2188,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
class SubstPackTypeBitfields {
friend class SubstPackType;
+ friend class SubstTemplateTypeParmPackType;
LLVM_PREFERRED_TYPE(TypeBitfields)
unsigned : NumTypeBits;
@@ -2197,23 +2198,12 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
/// However as this limit is somewhat easy to hit with template
/// metaprogramming we'd prefer to keep it as large as possible.
unsigned NumArgs : 16;
- };
- // FIXME: idiomatically, we want to have exact numbers, but that
- // ends up generating incorrect code (writes to
- // SubstTemplateTypeParmPackTypeBitfields.Index also update
- // bits in NumArgs).
- // enum { NumSubstPackTypeBits = NumTypeBits + 16 };
- static_assert(NumTypeBits + 16 <= 48);
- enum { NumSubstPackTypeBits = 48 };
-
- class SubstTemplateTypeParmPackTypeBitfields {
- friend class SubstTemplateTypeParmPackType;
-
- LLVM_PREFERRED_TYPE(SubstPackTypeBitfields)
- unsigned : NumSubstPackTypeBits;
// The index of the template parameter this substitution represents.
- unsigned Index : 16;
+ // Only used by SubstTemplateTypeParmPackType. We keep it in the same
+ // class to avoid dealing with complexities of bitfields that go over
+ // the size of `unsigned`.
+ unsigned SubstTemplTypeParmPackIndex : 16;
};
class TemplateSpecializationTypeBitfields {
@@ -2330,7 +2320,6 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
TemplateTypeParmTypeBitfields TemplateTypeParmTypeBits;
SubstTemplateTypeParmTypeBitfields SubstTemplateTypeParmTypeBits;
SubstPackTypeBitfields SubstPackTypeBits;
- SubstTemplateTypeParmPackTypeBitfields SubstTemplateTypeParmPackTypeBits;
TemplateSpecializationTypeBitfields TemplateSpecializationTypeBits;
DependentTemplateSpecializationTypeBitfields
DependentTemplateSpecializationTypeBits;
@@ -6757,7 +6746,9 @@ class SubstTemplateTypeParmPackType : public SubstPackType {
/// Returns the index of the replaced parameter in the associated declaration.
/// This should match the result of `getReplacedParameter()->getIndex()`.
- unsigned getIndex() const { return SubstTemplateTypeParmPackTypeBits.Index; }
+ unsigned getIndex() const {
+ return SubstPackTypeBits.SubstTemplTypeParmPackIndex;
+ }
// This substitution will be Final, which means the substitution will be fully
// sugared: it doesn't need to be resugared later.
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index c2f07184fb801..e0f7858aa33bb 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -4430,7 +4430,7 @@ SubstTemplateTypeParmPackType::SubstTemplateTypeParmPackType(
AssociatedDeclAndFinal(AssociatedDecl, Final) {
assert(AssociatedDecl != nullptr);
- SubstTemplateTypeParmPackTypeBits.Index = Index;
+ SubstPackTypeBits.SubstTemplTypeParmPackIndex = Index;
assert(getNumArgs() == ArgPack.pack_size() &&
"Parent bitfields in SubstPackType were overwritten."
"Check NumSubstPackTypeBits.");
More information about the cfe-commits
mailing list