Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2762d75
[Clang] Add a builtins that deduplicate types into a pack
ilya-biryukov Aug 23, 2024
241cbf4
Use wider int type to avoid truncating builting
ilya-biryukov Aug 5, 2025
01e5a36
remove includes added by Clangd
ilya-biryukov Aug 11, 2025
f05e54d
Pass the builtin name in the error message
ilya-biryukov Aug 11, 2025
0ebda66
Rename err_unsupported_builtin_template_pack_position to _expansion
ilya-biryukov Aug 11, 2025
7b1d3fb
Use isa<> with multiple template arguments
ilya-biryukov Aug 11, 2025
1f2f3d2
Remove unnecessary braces
ilya-biryukov Aug 11, 2025
ae0898b
Move helper into BuiltinTemplateDecl
ilya-biryukov Aug 11, 2025
6288f96
Update mangled name
ilya-biryukov Aug 11, 2025
1957947
Simplify an assertion
ilya-biryukov Aug 11, 2025
11b2e84
Remove redundant llvm:: prefix
ilya-biryukov Aug 11, 2025
0b5b579
Move check for the presence of template argument from parser into sema
ilya-biryukov Aug 11, 2025
00971e3
Add a comment with parameter name
ilya-biryukov Aug 11, 2025
61c57af
use if instead of continue
ilya-biryukov Aug 11, 2025
989fdfb
Revert added #includes
ilya-biryukov Aug 11, 2025
df0b041
Remove braces
ilya-biryukov Aug 11, 2025
1597f4a
Updated comments to use context instead of position
ilya-biryukov Aug 11, 2025
e409d0a
Remove redundant braces
ilya-biryukov Aug 11, 2025
bf72abe
remove redundant includes
ilya-biryukov Aug 11, 2025
7f0700c
Added a FIXME about reducing code duplication
ilya-biryukov Aug 11, 2025
d2bfa3e
Add a comment from the duplicated version
ilya-biryukov Aug 11, 2025
8ef79e9
Remove #includes
ilya-biryukov Aug 11, 2025
b81d054
Return Diag()
ilya-biryukov Aug 11, 2025
b01e1bb
Add comment, removed #includes
ilya-biryukov Aug 11, 2025
bdf79de
Fix a typo, add a comment
ilya-biryukov Aug 11, 2025
925e81e
Add Template.h, it is needed
ilya-biryukov Aug 11, 2025
028a222
Added requested tests
ilya-biryukov Aug 11, 2025
937027f
Remove more unused #includes
ilya-biryukov Aug 11, 2025
047847a
Fix test crashes
ilya-biryukov Aug 11, 2025
82351ee
Revert unintentional change
ilya-biryukov Aug 12, 2025
9fa61e1
Add a comment about SUBSTBUILTINPACK
ilya-biryukov Aug 12, 2025
43b557f
rename Dep to Dependence
ilya-biryukov Aug 12, 2025
62e8f95
Add a clarifying comment and a fixme for sugar
ilya-biryukov Aug 12, 2025
2c0dd40
Merge remote-tracking branch 'origin/main' into dedup
ilya-biryukov Aug 12, 2025
e047cc4
Fix compilation after merge
ilya-biryukov Aug 12, 2025
4496902
Format the code
ilya-biryukov Aug 12, 2025
1cf22a2
Add a release note
ilya-biryukov Aug 13, 2025
eae6f58
Add documentation about the new builtin to LanguageExtensions.rst
ilya-biryukov Aug 14, 2025
da7ad41
replace loop with all_of
ilya-biryukov Aug 14, 2025
cc97b95
Remove unnecessary spaces
ilya-biryukov Aug 14, 2025
53248dd
Merge remote-tracking branch 'origin/main' into dedup
ilya-biryukov Aug 14, 2025
1c0beeb
add newline to fix documentation build errors
ilya-biryukov Aug 14, 2025
5457414
Remove unnecessary check for TemplateName::DeducedTemplate
ilya-biryukov Aug 19, 2025
e0b40c7
Merge remote-tracking branch 'origin/main' into dedup
ilya-biryukov Aug 19, 2025
869f9a9
Use `getAsTemplateDecl()`
ilya-biryukov Aug 20, 2025
deedc41
Merge remote-tracking branch 'origin/main' into dedup
ilya-biryukov Aug 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang-tools-extra/clangd/unittests/FindTargetTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
31 changes: 31 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1810,6 +1810,37 @@ __make_integer_seq

This alias returns ``IntSeq`` instantiated with ``IntSeqT = T``and ``Ints`` being the pack ``0, ..., N - 1``.

__builtin_dedup_pack
--------------------

.. code-block:: c++

template <class... Ts>
using __builtin_dedup_pack = ...;

This alias takes a template parameter pack ``Ts`` and produces a new unexpanded pack containing the unique types
from ``Ts``, with the order of the first occurrence of each type preserved.
It is useful in template metaprogramming to normalize type lists.

The resulting pack can be expanded in contexts like template argument lists or base specifiers.

**Example of Use**:

.. code-block:: c++

template <typename...> struct TypeList;

// The resulting type is TypeList<int, double, char>
template <typename ...ExtraTypes>
using MyTypeList = TypeList<__builtin_dedup_pack<int, double, int, char, double, ExtraTypes...>...>;

**Limitations**:

* This builtin can only be used inside a template.
* The resulting pack is currently only supported for expansion in template argument lists and base specifiers.
* This builtin cannot be assigned to a template template parameter.


Type Trait Primitives
=====================

Expand Down
16 changes: 16 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ Non-comprehensive list of changes in this release
correct method to check for these features is to test for the ``__PTRAUTH__``
macro.

- Added a new builtin, ``__builtin_dedup_pack``, to remove duplicate types from a parameter pack.
This feature is particularly useful in template metaprogramming for normalizing type lists.
The builtin produces a new, unexpanded parameter pack that can be used in contexts like template
argument lists or base specifiers.

.. code-block:: c++

template <typename...> struct TypeList;

// The resulting type is TypeList<int, double, char>
using MyTypeList = TypeList<__builtin_dedup_pack<int, double, int, char, double>...>;

Currently, the use of ``__builtin_dedup_pack`` is limited to template arguments and base
specifiers, it also must be used within a template context.


New Compiler Flags
------------------
- New option ``-fno-sanitize-annotate-debug-info-traps`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,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};
Expand Down Expand Up @@ -1895,6 +1897,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,
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,10 @@ class BuiltinTemplateDecl : public TemplateDecl {
}

BuiltinTemplateKind getBuiltinTemplateKind() const { return BTK; }

bool isPackProducingBuiltinTemplate() const;
};
bool isPackProducingBuiltinTemplateName(TemplateName N);

/// Provides information about an explicit instantiation of a variable or class
/// template.
Expand Down
30 changes: 25 additions & 5 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,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);
Expand Down Expand Up @@ -1138,9 +1140,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(AttributedType,
{ TRY_TO(TraverseType(T->getModifiedType())); })
Expand Down Expand Up @@ -1481,9 +1484,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)); })

DEF_TRAVERSE_TYPELOC(ParenType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); })

Expand Down
80 changes: 64 additions & 16 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2210,20 +2210,24 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
unsigned PackIndex : 15;
};

class SubstTemplateTypeParmPackTypeBitfields {
class SubstPackTypeBitfields {
friend class SubstPackType;
friend class SubstTemplateTypeParmPackType;

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;

// The index of the template parameter this substitution represents.
// 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 {
Expand Down Expand Up @@ -2340,7 +2344,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
VectorTypeBitfields VectorTypeBits;
TemplateTypeParmTypeBitfields TemplateTypeParmTypeBits;
SubstTemplateTypeParmTypeBitfields SubstTemplateTypeParmTypeBits;
SubstTemplateTypeParmPackTypeBitfields SubstTemplateTypeParmPackTypeBits;
SubstPackTypeBitfields SubstPackTypeBits;
TemplateSpecializationTypeBitfields TemplateSpecializationTypeBits;
DependentTemplateSpecializationTypeBitfields
DependentTemplateSpecializationTypeBits;
Expand Down Expand Up @@ -6992,6 +6996,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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who owns these arguments? Are they part of the containing declaration? How do we know which set are ours?

It IS a touch confusing we're using the type-bits instead of an arrayref here, but justified :( (Nothing to do with this sentence, just lamenting...).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are they part of the containing declaration?

I think so, at least it's the case for SubstTemplateTypeParmPackType.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the contract is the same as SubstTemplateTypeParmPackType here.
It might be worth improving for both, I just tried to avoid scope creep in this review, though.


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.
///
Expand All @@ -7004,7 +7058,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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it looks like when you made this change you forgot to remove const TemplateArgument *Arguments; below. AFAICT this is vestigial now and is now part of SubstPackType.

This was flagged by static analysis b/c it is not being initialized in the constructor.

If there is a reason to keep it around a FIXME should be put in place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely need to remove it, thanks for mentioning this.
I'll send a commit to remove it.

friend class ASTContext;

/// A pointer to the set of template arguments that this
Expand All @@ -7030,21 +7084,17 @@ class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {

/// 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.
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,
Expand Down Expand Up @@ -7279,9 +7329,7 @@ class TemplateSpecializationType : public TypeWithKeyword,
TemplateSpecializationTypeBits.NumArgs};
}

bool isSugared() const {
return !isDependentType() || isCurrentInstantiation() || isTypeAlias();
}
bool isSugared() const;

QualType desugar() const {
return isTypeAlias() ? getAliasedType() : getCanonicalTypeInternal();
Expand Down
22 changes: 16 additions & 6 deletions clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -989,12 +989,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;
Expand Down
19 changes: 13 additions & 6 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -820,26 +820,33 @@ 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() }];
}
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(
associatedDecl, Index, Final, replacementPack);
}]>;
}

let Class = SubstBuiltinTemplatePackType in {
def : Creator<[{
return ctx.getSubstBuiltinTemplatePack(replacementPack);
}]>;
}

let Class = BuiltinType in {
def : Property<"kind", BuiltinTypeKind> {
let Read = [{ node->getKind() }];
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/BuiltinTemplates.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>]>;
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<"%0 cannot be used outside of template">;

def err_unsupported_builtin_template_pack_expansion
: 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<
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/TypeNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
Loading