Skip to content

[clang] Concepts: support pack expansions for type constraints #132626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ static std::vector<FixItHint> handleReturnType(const FunctionDecl *Function,
if (!TypeText)
return {};

SmallVector<const Expr *, 3> ExistingConstraints;
SmallVector<AssociatedConstraint, 3> ExistingConstraints;
Function->getAssociatedConstraints(ExistingConstraints);
if (!ExistingConstraints.empty()) {
// FIXME - Support adding new constraints to existing ones. Do we need to
Expand Down Expand Up @@ -404,7 +404,7 @@ handleTrailingTemplateType(const FunctionTemplateDecl *FunctionTemplate,
if (!ConditionText)
return {};

SmallVector<const Expr *, 3> ExistingConstraints;
SmallVector<AssociatedConstraint, 3> ExistingConstraints;
Function->getAssociatedConstraints(ExistingConstraints);
if (!ExistingConstraints.empty()) {
// FIXME - Support adding new constraints to existing ones. Do we need to
Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,9 @@ Bug Fixes to C++ Support
- Clang now uses the parameter location for abbreviated function templates in ``extern "C"``. (#GH46386)
- Clang will emit an error instead of crash when use co_await or co_yield in
C++26 braced-init-list template parameter initialization. (#GH78426)
- Improved fix for an issue with pack expansions of type constraints, where this
now also works if the constraint has non-type or template template parameters.
(#GH131798)
- Fixes matching of nested template template parameters. (#GH130362)
- Correctly diagnoses template template paramters which have a pack parameter
not in the last position.
Expand Down
11 changes: 9 additions & 2 deletions clang/include/clang/AST/ASTConcept.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,15 @@ class TypeConstraint {
/// type-constraint.
Expr *ImmediatelyDeclaredConstraint = nullptr;
ConceptReference *ConceptRef;
int ArgumentPackSubstitutionIndex;

public:
TypeConstraint(ConceptReference *ConceptRef,
Expr *ImmediatelyDeclaredConstraint)
Expr *ImmediatelyDeclaredConstraint,
int ArgumentPackSubstitutionIndex)
: ImmediatelyDeclaredConstraint(ImmediatelyDeclaredConstraint),
ConceptRef(ConceptRef) {}
ConceptRef(ConceptRef),
ArgumentPackSubstitutionIndex(ArgumentPackSubstitutionIndex) {}

/// \brief Get the immediately-declared constraint expression introduced by
/// this type-constraint, that is - the constraint expression that is added to
Expand All @@ -245,6 +248,10 @@ class TypeConstraint {

ConceptReference *getConceptReference() const { return ConceptRef; }

int getArgumentPackSubstitutionIndex() const {
return ArgumentPackSubstitutionIndex;
}

// FIXME: Instead of using these concept related functions the callers should
// directly work with the corresponding ConceptReference.
ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
Expand Down
4 changes: 1 addition & 3 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1798,9 +1798,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType
getSubstTemplateTypeParmType(QualType Replacement, Decl *AssociatedDecl,
unsigned Index,
std::optional<unsigned> PackIndex,
SubstTemplateTypeParmTypeFlag Flag =
SubstTemplateTypeParmTypeFlag::None) const;
std::optional<unsigned> PackIndex) const;
QualType getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
unsigned Index, bool Final,
const TemplateArgument &ArgPack);
Expand Down
17 changes: 15 additions & 2 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ class UnresolvedSetImpl;
class VarTemplateDecl;
enum class ImplicitParamKind;

// Holds a constraint expression along with a pack expansion index, if
// expanded.
struct AssociatedConstraint {
const Expr *ConstraintExpr;
int ArgumentPackSubstitutionIndex;

explicit AssociatedConstraint(const Expr *ConstraintExpr,
int ArgumentPackSubstitutionIndex = -1)
: ConstraintExpr(ConstraintExpr),
ArgumentPackSubstitutionIndex(ArgumentPackSubstitutionIndex) {}
};

/// The top declaration context.
class TranslationUnitDecl : public Decl,
public DeclContext,
Expand Down Expand Up @@ -2631,9 +2643,10 @@ class FunctionDecl : public DeclaratorDecl,
///
/// Use this instead of getTrailingRequiresClause for concepts APIs that
/// accept an ArrayRef of constraint expressions.
void getAssociatedConstraints(SmallVectorImpl<const Expr *> &AC) const {
void
getAssociatedConstraints(SmallVectorImpl<AssociatedConstraint> &AC) const {
if (auto *TRC = getTrailingRequiresClause())
AC.push_back(TRC);
AC.emplace_back(TRC);
}

/// Get the message that indicates why this function was deleted.
Expand Down
26 changes: 17 additions & 9 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ class TemplateParameterList final
///
/// The constraints in the resulting list are to be treated as if in a
/// conjunction ("and").
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const;
void getAssociatedConstraints(
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const;

bool hasAssociatedConstraints() const;

Expand Down Expand Up @@ -422,7 +423,8 @@ class TemplateDecl : public NamedDecl {
/// including constraint-expressions derived from the requires-clause,
/// trailing requires-clause (for functions and methods) and constrained
/// template parameters.
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const;
void getAssociatedConstraints(
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const;

bool hasAssociatedConstraints() const;

Expand Down Expand Up @@ -1341,7 +1343,8 @@ class TemplateTypeParmDecl final : public TypeDecl,
}

void setTypeConstraint(ConceptReference *CR,
Expr *ImmediatelyDeclaredConstraint);
Expr *ImmediatelyDeclaredConstraint,
int ArgumentPackSubstitutionIndex);

/// Determine whether this template parameter has a type-constraint.
bool hasTypeConstraint() const {
Expand All @@ -1353,9 +1356,11 @@ class TemplateTypeParmDecl final : public TypeDecl,
///
/// Use this instead of getTypeConstraint for concepts APIs that
/// accept an ArrayRef of constraint expressions.
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
void getAssociatedConstraints(
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const {
if (HasTypeConstraint)
AC.push_back(getTypeConstraint()->getImmediatelyDeclaredConstraint());
AC.emplace_back(getTypeConstraint()->getImmediatelyDeclaredConstraint(),
getTypeConstraint()->getArgumentPackSubstitutionIndex());
}

SourceRange getSourceRange() const override LLVM_READONLY;
Expand Down Expand Up @@ -1574,9 +1579,10 @@ class NonTypeTemplateParmDecl final
///
/// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for
/// concepts APIs that accept an ArrayRef of constraint expressions.
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
void getAssociatedConstraints(
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const {
if (Expr *E = getPlaceholderTypeConstraint())
AC.push_back(E);
AC.emplace_back(E);
}

// Implement isa/cast/dyncast/etc.
Expand Down Expand Up @@ -2169,7 +2175,8 @@ class ClassTemplatePartialSpecializationDecl
///
/// The constraints in the resulting list are to be treated as if in a
/// conjunction ("and").
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
void getAssociatedConstraints(
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const {
TemplateParams->getAssociatedConstraints(AC);
}

Expand Down Expand Up @@ -2943,7 +2950,8 @@ class VarTemplatePartialSpecializationDecl
///
/// The constraints in the resulting list are to be treated as if in a
/// conjunction ("and").
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
void getAssociatedConstraints(
llvm::SmallVectorImpl<AssociatedConstraint> &AC) const {
TemplateParams->getAssociatedConstraints(AC);
}

Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/AST/PropertiesBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ def Selector : PropertyType;
def SourceLocation : PropertyType;
def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; }
def ExprRef : SubclassPropertyType<"Expr", StmtRef>;
def SubstTemplateTypeParmTypeFlag : EnumPropertyType;
def TemplateArgument : PropertyType;
def TemplateArgumentKind : EnumPropertyType<"TemplateArgument::ArgKind">;
def TemplateName : DefaultValuePropertyType;
Expand Down
29 changes: 3 additions & 26 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1786,15 +1786,6 @@ enum class AutoTypeKeyword {
GNUAutoType
};

enum class SubstTemplateTypeParmTypeFlag {
None,

/// Whether to expand the pack using the stored PackIndex in place. This is
/// useful for e.g. substituting into an atomic constraint expression, where
/// that expression is part of an unexpanded pack.
ExpandPacksInPlace,
};

enum class ArraySizeModifier;
enum class ElaboratedTypeKeyword;
enum class VectorKind;
Expand Down Expand Up @@ -2164,9 +2155,6 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
LLVM_PREFERRED_TYPE(bool)
unsigned HasNonCanonicalUnderlyingType : 1;

LLVM_PREFERRED_TYPE(SubstTemplateTypeParmTypeFlag)
unsigned SubstitutionFlag : 1;

// The index of the template parameter this substitution represents.
unsigned Index : 15;

Expand Down Expand Up @@ -6409,8 +6397,7 @@ class SubstTemplateTypeParmType final
Decl *AssociatedDecl;

SubstTemplateTypeParmType(QualType Replacement, Decl *AssociatedDecl,
unsigned Index, std::optional<unsigned> PackIndex,
SubstTemplateTypeParmTypeFlag Flag);
unsigned Index, std::optional<unsigned> PackIndex);

public:
/// Gets the type that was substituted for the template
Expand Down Expand Up @@ -6439,31 +6426,21 @@ class SubstTemplateTypeParmType final
return SubstTemplateTypeParmTypeBits.PackIndex - 1;
}

SubstTemplateTypeParmTypeFlag getSubstitutionFlag() const {
return static_cast<SubstTemplateTypeParmTypeFlag>(
SubstTemplateTypeParmTypeBits.SubstitutionFlag);
}

bool isSugared() const { return true; }
QualType desugar() const { return getReplacementType(); }

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getReplacementType(), getAssociatedDecl(), getIndex(),
getPackIndex(), getSubstitutionFlag());
getPackIndex());
}

static void Profile(llvm::FoldingSetNodeID &ID, QualType Replacement,
const Decl *AssociatedDecl, unsigned Index,
std::optional<unsigned> PackIndex,
SubstTemplateTypeParmTypeFlag Flag) {
std::optional<unsigned> PackIndex) {
Replacement.Profile(ID);
ID.AddPointer(AssociatedDecl);
ID.AddInteger(Index);
ID.AddInteger(PackIndex ? *PackIndex - 1 : 0);
ID.AddInteger(llvm::to_underlying(Flag));
assert((Flag != SubstTemplateTypeParmTypeFlag::ExpandPacksInPlace ||
PackIndex) &&
"ExpandPacksInPlace needs a valid PackIndex");
}

static bool classof(const Type *T) {
Expand Down
5 changes: 1 addition & 4 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -842,14 +842,11 @@ let Class = SubstTemplateTypeParmType in {
def : Property<"PackIndex", Optional<UInt32>> {
let Read = [{ node->getPackIndex() }];
}
def : Property<"SubstitutionFlag", SubstTemplateTypeParmTypeFlag> {
let Read = [{ node->getSubstitutionFlag() }];
}

// The call to getCanonicalType here existed in ASTReader.cpp, too.
def : Creator<[{
return ctx.getSubstTemplateTypeParmType(
replacementType, associatedDecl, Index, PackIndex, SubstitutionFlag);
replacementType, associatedDecl, Index, PackIndex);
}]>;
}

Expand Down
24 changes: 13 additions & 11 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -11351,7 +11351,6 @@ class Sema final : public SemaBase {
ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
QualType ConstrainedType,
SourceLocation EllipsisLoc);

bool AttachTypeConstraint(AutoTypeLoc TL,
Expand Down Expand Up @@ -14552,13 +14551,14 @@ class Sema final : public SemaBase {
/// \returns true if an error occurred and satisfaction could not be checked,
/// false otherwise.
bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
const NamedDecl *Template,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
llvm::SmallVector<Expr *, 4> Converted;
return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted,
TemplateArgLists, TemplateIDRange,
Satisfaction);
return CheckConstraintSatisfaction(Template, AssociatedConstraints,
Converted, TemplateArgLists,
TemplateIDRange, Satisfaction);
}

/// \brief Check whether the given list of constraint expressions are
Expand All @@ -14584,7 +14584,8 @@ class Sema final : public SemaBase {
/// \returns true if an error occurred and satisfaction could not be checked,
/// false otherwise.
bool CheckConstraintSatisfaction(
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
const NamedDecl *Template,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgList,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
Expand Down Expand Up @@ -14662,7 +14663,7 @@ class Sema final : public SemaBase {

const NormalizedConstraint *getNormalizedAssociatedConstraints(
const NamedDecl *ConstrainedDecl,
ArrayRef<const Expr *> AssociatedConstraints);
ArrayRef<AssociatedConstraint> AssociatedConstraints);

/// \brief Check whether the given declaration's associated constraints are
/// at least as constrained than another declaration's according to the
Expand All @@ -14673,17 +14674,18 @@ class Sema final : public SemaBase {
///
/// \returns true if an error occurred, false otherwise.
bool IsAtLeastAsConstrained(const NamedDecl *D1,
MutableArrayRef<const Expr *> AC1,
MutableArrayRef<AssociatedConstraint> AC1,
const NamedDecl *D2,
MutableArrayRef<const Expr *> AC2, bool &Result);
MutableArrayRef<AssociatedConstraint> AC2,
bool &Result);

/// If D1 was not at least as constrained as D2, but would've been if a pair
/// of atomic constraints involved had been declared in a concept and not
/// repeated in two separate places in code.
/// \returns true if such a diagnostic was emitted, false otherwise.
bool MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
const NamedDecl *D1, ArrayRef<const Expr *> AC1, const NamedDecl *D2,
ArrayRef<const Expr *> AC2);
const NamedDecl *D1, ArrayRef<AssociatedConstraint> AC1,
const NamedDecl *D2, ArrayRef<AssociatedConstraint> AC2);

private:
/// Caches pairs of template-like decls whose associated constraints were
Expand Down
11 changes: 7 additions & 4 deletions clang/include/clang/Sema/SemaConcept.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ struct NormalizedConstraint {

private:
static std::optional<NormalizedConstraint>
fromConstraintExprs(Sema &S, const NamedDecl *D, ArrayRef<const Expr *> E);
fromAssociatedConstraints(Sema &S, const NamedDecl *D,
ArrayRef<AssociatedConstraint> ACs);
static std::optional<NormalizedConstraint>
fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E);
};
Expand All @@ -138,7 +139,7 @@ struct alignas(ConstraintAlignment) FoldExpandedConstraint {

const NormalizedConstraint *getNormalizedAssociatedConstraints(
Sema &S, const NamedDecl *ConstrainedDecl,
ArrayRef<const Expr *> AssociatedConstraints);
ArrayRef<AssociatedConstraint> AssociatedConstraints);

/// \brief SubsumptionChecker establishes subsumption
/// between two set of constraints.
Expand All @@ -149,8 +150,10 @@ class SubsumptionChecker {

SubsumptionChecker(Sema &SemaRef, SubsumptionCallable Callable = {});

std::optional<bool> Subsumes(const NamedDecl *DP, ArrayRef<const Expr *> P,
const NamedDecl *DQ, ArrayRef<const Expr *> Q);
std::optional<bool> Subsumes(const NamedDecl *DP,
ArrayRef<AssociatedConstraint> P,
const NamedDecl *DQ,
ArrayRef<AssociatedConstraint> Q);

bool Subsumes(const NormalizedConstraint *P, const NormalizedConstraint *Q);

Expand Down
7 changes: 3 additions & 4 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5447,11 +5447,10 @@ QualType ASTContext::getHLSLAttributedResourceType(
/// Retrieve a substitution-result type.
QualType ASTContext::getSubstTemplateTypeParmType(
QualType Replacement, Decl *AssociatedDecl, unsigned Index,
std::optional<unsigned> PackIndex,
SubstTemplateTypeParmTypeFlag Flag) const {
std::optional<unsigned> PackIndex) const {
llvm::FoldingSetNodeID ID;
SubstTemplateTypeParmType::Profile(ID, Replacement, AssociatedDecl, Index,
PackIndex, Flag);
PackIndex);
void *InsertPos = nullptr;
SubstTemplateTypeParmType *SubstParm =
SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos);
Expand All @@ -5461,7 +5460,7 @@ QualType ASTContext::getSubstTemplateTypeParmType(
!Replacement.isCanonical()),
alignof(SubstTemplateTypeParmType));
SubstParm = new (Mem) SubstTemplateTypeParmType(Replacement, AssociatedDecl,
Index, PackIndex, Flag);
Index, PackIndex);
Types.push_back(SubstParm);
SubstTemplateTypeParmTypes.InsertNode(SubstParm, InsertPos);
}
Expand Down
Loading
Loading