Skip to content

[clang] support pack expansions for trailing requires clauses #133190

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 1 commit into from
Apr 3, 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
6 changes: 3 additions & 3 deletions clang-tools-extra/clangd/refactor/tweaks/ExtractVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ computeReferencedDecls(const clang::Expr *Expr) {
TraverseLambdaCapture(LExpr, &Capture, Initializer);
}

if (clang::Expr *const RequiresClause =
LExpr->getTrailingRequiresClause()) {
TraverseStmt(RequiresClause);
if (const clang::Expr *RequiresClause =
LExpr->getTrailingRequiresClause().ConstraintExpr) {
TraverseStmt(const_cast<clang::Expr *>(RequiresClause));
}

for (auto *const TemplateParam : LExpr->getExplicitTemplateParameters())
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ Bug Fixes to C++ Support
- 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)
- Fix crash when evaluating the trailing requires clause of generic lambdas which are part of
a pack expansion.
- 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
8 changes: 8 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2906,6 +2906,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// that they may be used in declarations of the same template.
bool isSameTemplateParameter(const NamedDecl *X, const NamedDecl *Y) const;

/// Determine whether two 'requires' expressions are similar enough that they
/// may be used in re-declarations.
///
/// Use of 'requires' isn't mandatory, works with constraints expressed in
/// other ways too.
bool isSameAssociatedConstraint(const AssociatedConstraint &ACX,
const AssociatedConstraint &ACY) const;

/// Determine whether two 'requires' expressions are similar enough that they
/// may be used in re-declarations.
///
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,8 @@ class ASTNodeTraverser
for (const auto *Parameter : D->parameters())
Visit(Parameter);

if (const Expr *TRC = D->getTrailingRequiresClause())
Visit(TRC);
if (const AssociatedConstraint &TRC = D->getTrailingRequiresClause())
Visit(TRC.ConstraintExpr);

if (Traversal == TK_IgnoreUnlessSpelledInSource && D->isDefaulted())
return;
Expand Down
37 changes: 19 additions & 18 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,19 @@ enum class ImplicitParamKind;
// Holds a constraint expression along with a pack expansion index, if
// expanded.
struct AssociatedConstraint {
const Expr *ConstraintExpr;
int ArgumentPackSubstitutionIndex;
const Expr *ConstraintExpr = nullptr;
int ArgumentPackSubstitutionIndex = -1;

constexpr AssociatedConstraint() = default;

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

explicit operator bool() const { return ConstraintExpr != nullptr; }

bool isNull() const { return !operator bool(); }
};

/// The top declaration context.
Expand Down Expand Up @@ -754,7 +760,7 @@ class DeclaratorDecl : public ValueDecl {
// and constrained function decls.
struct ExtInfo : public QualifierInfo {
TypeSourceInfo *TInfo = nullptr;
Expr *TrailingRequiresClause = nullptr;
AssociatedConstraint TrailingRequiresClause;
};

llvm::PointerUnion<TypeSourceInfo *, ExtInfo *> DeclInfo;
Expand Down Expand Up @@ -823,17 +829,12 @@ class DeclaratorDecl : public ValueDecl {
/// \brief Get the constraint-expression introduced by the trailing
/// requires-clause in the function/member declaration, or null if no
/// requires-clause was provided.
Expr *getTrailingRequiresClause() {
return hasExtInfo() ? getExtInfo()->TrailingRequiresClause
: nullptr;
}

const Expr *getTrailingRequiresClause() const {
return hasExtInfo() ? getExtInfo()->TrailingRequiresClause
: nullptr;
const AssociatedConstraint &getTrailingRequiresClause() const {
static constexpr AssociatedConstraint Null;
return hasExtInfo() ? getExtInfo()->TrailingRequiresClause : Null;
}

void setTrailingRequiresClause(Expr *TrailingRequiresClause);
void setTrailingRequiresClause(const AssociatedConstraint &AC);

unsigned getNumTemplateParameterLists() const {
return hasExtInfo() ? getExtInfo()->NumTemplParamLists : 0;
Expand Down Expand Up @@ -2102,7 +2103,7 @@ class FunctionDecl : public DeclaratorDecl,
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, StorageClass S, bool UsesFPIntrin,
bool isInlineSpecified, ConstexprSpecKind ConstexprKind,
Expr *TrailingRequiresClause = nullptr);
const AssociatedConstraint &TrailingRequiresClause);

using redeclarable_base = Redeclarable<FunctionDecl>;

Expand Down Expand Up @@ -2138,7 +2139,7 @@ class FunctionDecl : public DeclaratorDecl,
TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin = false,
bool isInlineSpecified = false, bool hasWrittenPrototype = true,
ConstexprSpecKind ConstexprKind = ConstexprSpecKind::Unspecified,
Expr *TrailingRequiresClause = nullptr) {
const AssociatedConstraint &TrailingRequiresClause = {}) {
DeclarationNameInfo NameInfo(N, NLoc);
return FunctionDecl::Create(C, DC, StartLoc, NameInfo, T, TInfo, SC,
UsesFPIntrin, isInlineSpecified,
Expand All @@ -2151,7 +2152,7 @@ class FunctionDecl : public DeclaratorDecl,
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
StorageClass SC, bool UsesFPIntrin, bool isInlineSpecified,
bool hasWrittenPrototype, ConstexprSpecKind ConstexprKind,
Expr *TrailingRequiresClause);
const AssociatedConstraint &TrailingRequiresClause);

static FunctionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);

Expand Down Expand Up @@ -2644,9 +2645,9 @@ class FunctionDecl : public DeclaratorDecl,
/// Use this instead of getTrailingRequiresClause for concepts APIs that
/// accept an ArrayRef of constraint expressions.
void
getAssociatedConstraints(SmallVectorImpl<AssociatedConstraint> &AC) const {
if (auto *TRC = getTrailingRequiresClause())
AC.emplace_back(TRC);
getAssociatedConstraints(SmallVectorImpl<AssociatedConstraint> &ACs) const {
if (const AssociatedConstraint &AC = getTrailingRequiresClause())
ACs.emplace_back(AC);
}

/// Get the message that indicates why this function was deleted.
Expand Down
20 changes: 10 additions & 10 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -1974,7 +1974,7 @@ class CXXDeductionGuideDecl : public FunctionDecl {
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation,
CXXConstructorDecl *Ctor, DeductionCandidate Kind,
Expr *TrailingRequiresClause,
const AssociatedConstraint &TrailingRequiresClause,
const CXXDeductionGuideDecl *GeneratedFrom,
SourceDeductionGuideKind SourceKind)
: FunctionDecl(CXXDeductionGuide, C, DC, StartLoc, NameInfo, T, TInfo,
Expand Down Expand Up @@ -2007,7 +2007,7 @@ class CXXDeductionGuideDecl : public FunctionDecl {
TypeSourceInfo *TInfo, SourceLocation EndLocation,
CXXConstructorDecl *Ctor = nullptr,
DeductionCandidate Kind = DeductionCandidate::Normal,
Expr *TrailingRequiresClause = nullptr,
const AssociatedConstraint &TrailingRequiresClause = {},
const CXXDeductionGuideDecl *SourceDG = nullptr,
SourceDeductionGuideKind SK = SourceDeductionGuideKind::None);

Expand Down Expand Up @@ -2115,7 +2115,7 @@ class CXXMethodDecl : public FunctionDecl {
QualType T, TypeSourceInfo *TInfo, StorageClass SC,
bool UsesFPIntrin, bool isInline,
ConstexprSpecKind ConstexprKind, SourceLocation EndLocation,
Expr *TrailingRequiresClause = nullptr)
const AssociatedConstraint &TrailingRequiresClause = {})
: FunctionDecl(DK, C, RD, StartLoc, NameInfo, T, TInfo, SC, UsesFPIntrin,
isInline, ConstexprKind, TrailingRequiresClause) {
if (EndLocation.isValid())
Expand All @@ -2128,7 +2128,7 @@ class CXXMethodDecl : public FunctionDecl {
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
StorageClass SC, bool UsesFPIntrin, bool isInline,
ConstexprSpecKind ConstexprKind, SourceLocation EndLocation,
Expr *TrailingRequiresClause = nullptr);
const AssociatedConstraint &TrailingRequiresClause = {});

static CXXMethodDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);

Expand Down Expand Up @@ -2596,7 +2596,7 @@ class CXXConstructorDecl final
bool UsesFPIntrin, bool isInline,
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
InheritedConstructor Inherited,
Expr *TrailingRequiresClause);
const AssociatedConstraint &TrailingRequiresClause);

void anchor() override;

Expand Down Expand Up @@ -2639,7 +2639,7 @@ class CXXConstructorDecl final
ExplicitSpecifier ES, bool UsesFPIntrin, bool isInline,
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
InheritedConstructor Inherited = InheritedConstructor(),
Expr *TrailingRequiresClause = nullptr);
const AssociatedConstraint &TrailingRequiresClause = {});

void setExplicitSpecifier(ExplicitSpecifier ES) {
assert((!ES.getExpr() ||
Expand Down Expand Up @@ -2858,7 +2858,7 @@ class CXXDestructorDecl : public CXXMethodDecl {
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, bool UsesFPIntrin, bool isInline,
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
Expr *TrailingRequiresClause = nullptr)
const AssociatedConstraint &TrailingRequiresClause = {})
: CXXMethodDecl(CXXDestructor, C, RD, StartLoc, NameInfo, T, TInfo,
SC_None, UsesFPIntrin, isInline, ConstexprKind,
SourceLocation(), TrailingRequiresClause) {
Expand All @@ -2873,7 +2873,7 @@ class CXXDestructorDecl : public CXXMethodDecl {
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared,
ConstexprSpecKind ConstexprKind,
Expr *TrailingRequiresClause = nullptr);
const AssociatedConstraint &TrailingRequiresClause = {});
static CXXDestructorDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);

void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);
Expand Down Expand Up @@ -2919,7 +2919,7 @@ class CXXConversionDecl : public CXXMethodDecl {
TypeSourceInfo *TInfo, bool UsesFPIntrin, bool isInline,
ExplicitSpecifier ES, ConstexprSpecKind ConstexprKind,
SourceLocation EndLocation,
Expr *TrailingRequiresClause = nullptr)
const AssociatedConstraint &TrailingRequiresClause = {})
: CXXMethodDecl(CXXConversion, C, RD, StartLoc, NameInfo, T, TInfo,
SC_None, UsesFPIntrin, isInline, ConstexprKind,
EndLocation, TrailingRequiresClause),
Expand All @@ -2937,7 +2937,7 @@ class CXXConversionDecl : public CXXMethodDecl {
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
bool UsesFPIntrin, bool isInline, ExplicitSpecifier ES,
ConstexprSpecKind ConstexprKind, SourceLocation EndLocation,
Expr *TrailingRequiresClause = nullptr);
const AssociatedConstraint &TrailingRequiresClause = {});
static CXXConversionDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);

ExplicitSpecifier getExplicitSpecifier() {
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -2129,7 +2129,7 @@ class LambdaExpr final : public Expr,
ArrayRef<NamedDecl *> getExplicitTemplateParameters() const;

/// Get the trailing requires clause, if any.
Expr *getTrailingRequiresClause() const;
const AssociatedConstraint &getTrailingRequiresClause() const;

/// Whether this is a generic lambda.
bool isGenericLambda() const { return getTemplateParameterList(); }
Expand Down
9 changes: 6 additions & 3 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2253,8 +2253,10 @@ bool RecursiveASTVisitor<Derived>::TraverseFunctionHelper(FunctionDecl *D) {
}

// Visit the trailing requires clause, if any.
if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) {
TRY_TO(TraverseStmt(TrailingRequiresClause));
if (const AssociatedConstraint &TrailingRequiresClause =
D->getTrailingRequiresClause()) {
TRY_TO(TraverseStmt(
const_cast<Expr *>(TrailingRequiresClause.ConstraintExpr)));
}

if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) {
Expand Down Expand Up @@ -2768,7 +2770,8 @@ DEF_TRAVERSE_STMT(LambdaExpr, {

if (S->hasExplicitResultType())
TRY_TO(TraverseTypeLoc(Proto.getReturnLoc()));
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getTrailingRequiresClause());
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(
const_cast<Expr *>(S->getTrailingRequiresClause().ConstraintExpr));

TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody());
}
Expand Down
14 changes: 8 additions & 6 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8866,12 +8866,14 @@ class Sema final : public SemaBase {
CXXMethodDecl *CallOperator, CXXRecordDecl *Class,
TemplateParameterList *TemplateParams);

void CompleteLambdaCallOperator(
CXXMethodDecl *Method, SourceLocation LambdaLoc,
SourceLocation CallOperatorLoc, Expr *TrailingRequiresClause,
TypeSourceInfo *MethodTyInfo, ConstexprSpecKind ConstexprKind,
StorageClass SC, ArrayRef<ParmVarDecl *> Params,
bool HasExplicitResultType);
void
CompleteLambdaCallOperator(CXXMethodDecl *Method, SourceLocation LambdaLoc,
SourceLocation CallOperatorLoc,
const AssociatedConstraint &TrailingRequiresClause,
TypeSourceInfo *MethodTyInfo,
ConstexprSpecKind ConstexprKind, StorageClass SC,
ArrayRef<ParmVarDecl *> Params,
bool HasExplicitResultType);

/// Returns true if the explicit object parameter was invalid.
bool DiagnoseInvalidExplicitObjectParameterInLambda(CXXMethodDecl *Method,
Expand Down
13 changes: 11 additions & 2 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7070,6 +7070,15 @@ bool ASTContext::hasSameTemplateName(const TemplateName &X,
getCanonicalTemplateName(Y, IgnoreDeduced);
}

bool ASTContext::isSameAssociatedConstraint(
const AssociatedConstraint &ACX, const AssociatedConstraint &ACY) const {
if (ACX.ArgumentPackSubstitutionIndex != ACY.ArgumentPackSubstitutionIndex)
return false;
if (!isSameConstraintExpr(ACX.ConstraintExpr, ACY.ConstraintExpr))
return false;
return true;
}

bool ASTContext::isSameConstraintExpr(const Expr *XCE, const Expr *YCE) const {
if (!XCE != !YCE)
return false;
Expand Down Expand Up @@ -7386,8 +7395,8 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
return false;
}

if (!isSameConstraintExpr(FuncX->getTrailingRequiresClause(),
FuncY->getTrailingRequiresClause()))
if (!isSameAssociatedConstraint(FuncX->getTrailingRequiresClause(),
FuncY->getTrailingRequiresClause()))
return false;

auto GetTypeAsWritten = [](const FunctionDecl *FD) {
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3915,8 +3915,9 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
auto ToEndLoc = importChecked(Err, D->getEndLoc());
auto ToDefaultLoc = importChecked(Err, D->getDefaultLoc());
auto ToQualifierLoc = importChecked(Err, D->getQualifierLoc());
auto TrailingRequiresClause =
importChecked(Err, D->getTrailingRequiresClause());
AssociatedConstraint TrailingRequiresClause = D->getTrailingRequiresClause();
TrailingRequiresClause.ConstraintExpr =
importChecked(Err, TrailingRequiresClause.ConstraintExpr);
if (Err)
return std::move(Err);

Expand Down
15 changes: 8 additions & 7 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2009,8 +2009,8 @@ void DeclaratorDecl::setQualifierInfo(NestedNameSpecifierLoc QualifierLoc) {
}
}

void DeclaratorDecl::setTrailingRequiresClause(Expr *TrailingRequiresClause) {
assert(TrailingRequiresClause);
void DeclaratorDecl::setTrailingRequiresClause(const AssociatedConstraint &AC) {
assert(AC);
// Make sure the extended decl info is allocated.
if (!hasExtInfo()) {
// Save (non-extended) type source info pointer.
Expand All @@ -2021,7 +2021,7 @@ void DeclaratorDecl::setTrailingRequiresClause(Expr *TrailingRequiresClause) {
getExtInfo()->TInfo = savedTInfo;
}
// Set requires clause info.
getExtInfo()->TrailingRequiresClause = TrailingRequiresClause;
getExtInfo()->TrailingRequiresClause = AC;
}

void DeclaratorDecl::setTemplateParameterListsInfo(
Expand Down Expand Up @@ -3047,7 +3047,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
TypeSourceInfo *TInfo, StorageClass S,
bool UsesFPIntrin, bool isInlineSpecified,
ConstexprSpecKind ConstexprKind,
Expr *TrailingRequiresClause)
const AssociatedConstraint &TrailingRequiresClause)
: DeclaratorDecl(DK, DC, NameInfo.getLoc(), NameInfo.getName(), T, TInfo,
StartLoc),
DeclContext(DK), redeclarable_base(C), Body(), ODRHash(0),
Expand Down Expand Up @@ -3571,7 +3571,7 @@ bool FunctionDecl::isMemberLikeConstrainedFriend() const {
// If these friends don't have constraints, they aren't constrained, and
// thus don't fall under temp.friend p9. Else the simple presence of a
// constraint makes them unique.
return getTrailingRequiresClause();
return !getTrailingRequiresClause().isNull();
}

return FriendConstraintRefersToEnclosingTemplate();
Expand Down Expand Up @@ -5453,7 +5453,7 @@ FunctionDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin,
bool isInlineSpecified, bool hasWrittenPrototype,
ConstexprSpecKind ConstexprKind,
Expr *TrailingRequiresClause) {
const AssociatedConstraint &TrailingRequiresClause) {
FunctionDecl *New = new (C, DC) FunctionDecl(
Function, C, DC, StartLoc, NameInfo, T, TInfo, SC, UsesFPIntrin,
isInlineSpecified, ConstexprKind, TrailingRequiresClause);
Expand All @@ -5464,7 +5464,8 @@ FunctionDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
FunctionDecl *FunctionDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) FunctionDecl(
Function, C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(),
nullptr, SC_None, false, false, ConstexprSpecKind::Unspecified, nullptr);
nullptr, SC_None, false, false, ConstexprSpecKind::Unspecified,
/*TrailingRequiresClause=*/{});
}

BlockDecl *BlockDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L) {
Expand Down
Loading
Loading