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

This commit is contained in:
Matheus Izvekov 2025-04-03 12:36:15 -03:00 committed by GitHub
parent c1ada72b09
commit 49fd0bf35d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 204 additions and 133 deletions

View File

@ -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())

View File

@ -385,6 +385,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.

View File

@ -2907,6 +2907,14 @@ public:
/// 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.
///

View File

@ -538,8 +538,8 @@ public:
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;

View File

@ -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.
@ -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;
@ -823,17 +829,12 @@ public:
/// \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 AssociatedConstraint &getTrailingRequiresClause() const {
static constexpr AssociatedConstraint Null;
return hasExtInfo() ? getExtInfo()->TrailingRequiresClause : Null;
}
const Expr *getTrailingRequiresClause() const {
return hasExtInfo() ? getExtInfo()->TrailingRequiresClause
: nullptr;
}
void setTrailingRequiresClause(Expr *TrailingRequiresClause);
void setTrailingRequiresClause(const AssociatedConstraint &AC);
unsigned getNumTemplateParameterLists() const {
return hasExtInfo() ? getExtInfo()->NumTemplParamLists : 0;
@ -2102,7 +2103,7 @@ protected:
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>;
@ -2138,7 +2139,7 @@ public:
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,
@ -2151,7 +2152,7 @@ public:
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);
@ -2644,9 +2645,9 @@ public:
/// 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.

View File

@ -1974,7 +1974,7 @@ private:
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,
@ -2007,7 +2007,7 @@ public:
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);
@ -2115,7 +2115,7 @@ protected:
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())
@ -2128,7 +2128,7 @@ public:
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);
@ -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;
@ -2639,7 +2639,7 @@ public:
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() ||
@ -2859,7 +2859,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) {
@ -2874,7 +2874,7 @@ public:
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);
@ -2925,7 +2925,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),
@ -2943,7 +2943,7 @@ public:
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() {

View File

@ -2129,7 +2129,7 @@ public:
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(); }

View File

@ -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)) {
@ -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());
}

View File

@ -8867,11 +8867,13 @@ public:
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,
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.

View File

@ -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;
@ -7386,7 +7395,7 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
return false;
}
if (!isSameConstraintExpr(FuncX->getTrailingRequiresClause(),
if (!isSameAssociatedConstraint(FuncX->getTrailingRequiresClause(),
FuncY->getTrailingRequiresClause()))
return false;

View File

@ -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);

View File

@ -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.
@ -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(
@ -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),
@ -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();
@ -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);
@ -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) {

View File

@ -2304,7 +2304,7 @@ CXXDeductionGuideDecl *CXXDeductionGuideDecl::Create(
ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation, CXXConstructorDecl *Ctor,
DeductionCandidate Kind, Expr *TrailingRequiresClause,
DeductionCandidate Kind, const AssociatedConstraint &TrailingRequiresClause,
const CXXDeductionGuideDecl *GeneratedFrom,
SourceDeductionGuideKind SourceKind) {
return new (C, DC) CXXDeductionGuideDecl(
@ -2318,7 +2318,7 @@ CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
C, /*DC=*/nullptr, SourceLocation(), ExplicitSpecifier(),
DeclarationNameInfo(), QualType(), /*TInfo=*/nullptr, SourceLocation(),
/*Ctor=*/nullptr, DeductionCandidate::Normal,
/*TrailingRequiresClause=*/nullptr,
/*TrailingRequiresClause=*/{},
/*GeneratedFrom=*/nullptr, SourceDeductionGuideKind::None);
}
@ -2427,7 +2427,7 @@ CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin,
bool isInline, ConstexprSpecKind ConstexprKind,
SourceLocation EndLocation,
Expr *TrailingRequiresClause) {
const AssociatedConstraint &TrailingRequiresClause) {
return new (C, RD) CXXMethodDecl(
CXXMethod, C, RD, StartLoc, NameInfo, T, TInfo, SC, UsesFPIntrin,
isInline, ConstexprKind, EndLocation, TrailingRequiresClause);
@ -2435,10 +2435,11 @@ CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
CXXMethodDecl *CXXMethodDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) CXXMethodDecl(
CXXMethod, C, nullptr, SourceLocation(), DeclarationNameInfo(),
QualType(), nullptr, SC_None, false, false,
ConstexprSpecKind::Unspecified, SourceLocation(), nullptr);
return new (C, ID)
CXXMethodDecl(CXXMethod, C, nullptr, SourceLocation(),
DeclarationNameInfo(), QualType(), nullptr, SC_None, false,
false, ConstexprSpecKind::Unspecified, SourceLocation(),
/*TrailingRequiresClause=*/{});
}
CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
@ -2834,7 +2835,8 @@ CXXConstructorDecl::CXXConstructorDecl(
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
ExplicitSpecifier ES, bool UsesFPIntrin, bool isInline,
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
InheritedConstructor Inherited, Expr *TrailingRequiresClause)
InheritedConstructor Inherited,
const AssociatedConstraint &TrailingRequiresClause)
: CXXMethodDecl(CXXConstructor, C, RD, StartLoc, NameInfo, T, TInfo,
SC_None, UsesFPIntrin, isInline, ConstexprKind,
SourceLocation(), TrailingRequiresClause) {
@ -2861,7 +2863,7 @@ CXXConstructorDecl *CXXConstructorDecl::CreateDeserialized(ASTContext &C,
auto *Result = new (C, ID, Extra) CXXConstructorDecl(
C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr,
ExplicitSpecifier(), false, false, false, ConstexprSpecKind::Unspecified,
InheritedConstructor(), nullptr);
InheritedConstructor(), /*TrailingRequiresClause=*/{});
Result->setInheritingConstructor(isInheritingConstructor);
Result->CXXConstructorDeclBits.HasTrailingExplicitSpecifier =
hasTrailingExplicit;
@ -2874,7 +2876,8 @@ CXXConstructorDecl *CXXConstructorDecl::Create(
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
ExplicitSpecifier ES, bool UsesFPIntrin, bool isInline,
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
InheritedConstructor Inherited, Expr *TrailingRequiresClause) {
InheritedConstructor Inherited,
const AssociatedConstraint &TrailingRequiresClause) {
assert(NameInfo.getName().getNameKind()
== DeclarationName::CXXConstructorName &&
"Name must refer to a constructor");
@ -3000,14 +3003,16 @@ CXXDestructorDecl *CXXDestructorDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) CXXDestructorDecl(
C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr,
false, false, false, ConstexprSpecKind::Unspecified, nullptr);
false, false, false, ConstexprSpecKind::Unspecified,
/*TrailingRequiresClause=*/{});
}
CXXDestructorDecl *CXXDestructorDecl::Create(
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared,
ConstexprSpecKind ConstexprKind, Expr *TrailingRequiresClause) {
ConstexprSpecKind ConstexprKind,
const AssociatedConstraint &TrailingRequiresClause) {
assert(NameInfo.getName().getNameKind()
== DeclarationName::CXXDestructorName &&
"Name must refer to a destructor");
@ -3062,7 +3067,7 @@ CXXConversionDecl *CXXConversionDecl::CreateDeserialized(ASTContext &C,
return new (C, ID) CXXConversionDecl(
C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr,
false, false, ExplicitSpecifier(), ConstexprSpecKind::Unspecified,
SourceLocation(), nullptr);
SourceLocation(), /*TrailingRequiresClause=*/{});
}
CXXConversionDecl *CXXConversionDecl::Create(
@ -3070,7 +3075,7 @@ CXXConversionDecl *CXXConversionDecl::Create(
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
bool UsesFPIntrin, bool isInline, ExplicitSpecifier ES,
ConstexprSpecKind ConstexprKind, SourceLocation EndLocation,
Expr *TrailingRequiresClause) {
const AssociatedConstraint &TrailingRequiresClause) {
assert(NameInfo.getName().getNameKind()
== DeclarationName::CXXConversionFunctionName &&
"Name must refer to a conversion function");

View File

@ -842,10 +842,14 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) {
}
Out << Proto;
if (Expr *TrailingRequiresClause = D->getTrailingRequiresClause()) {
if (const AssociatedConstraint &TrailingRequiresClause =
D->getTrailingRequiresClause()) {
Out << " requires ";
TrailingRequiresClause->printPretty(Out, nullptr, SubPolicy, Indentation,
"\n", &Context);
// FIXME: The printer could support printing expressions and types as if
// expanded by an index. Pass in the ArgumentPackSubstitutionIndex when
// that's supported.
TrailingRequiresClause.ConstraintExpr->printPretty(
Out, nullptr, SubPolicy, Indentation, "\n", &Context);
}
} else {
Ty.print(Out, Policy, Proto);

View File

@ -291,7 +291,7 @@ void TemplateDecl::getAssociatedConstraints(
llvm::SmallVectorImpl<AssociatedConstraint> &ACs) const {
TemplateParams->getAssociatedConstraints(ACs);
if (auto *FD = dyn_cast_or_null<FunctionDecl>(getTemplatedDecl()))
if (const Expr *TRC = FD->getTrailingRequiresClause())
if (const AssociatedConstraint &TRC = FD->getTrailingRequiresClause())
ACs.emplace_back(TRC);
}
@ -299,7 +299,7 @@ bool TemplateDecl::hasAssociatedConstraints() const {
if (TemplateParams->hasAssociatedConstraints())
return true;
if (auto *FD = dyn_cast_or_null<FunctionDecl>(getTemplatedDecl()))
return FD->getTrailingRequiresClause();
return static_cast<bool>(FD->getTrailingRequiresClause());
return false;
}

View File

@ -1402,7 +1402,7 @@ ArrayRef<NamedDecl *> LambdaExpr::getExplicitTemplateParameters() const {
return Record->getLambdaExplicitTemplateParameters();
}
Expr *LambdaExpr::getTrailingRequiresClause() const {
const AssociatedConstraint &LambdaExpr::getTrailingRequiresClause() const {
return getCallOperator()->getTrailingRequiresClause();
}

View File

@ -3781,7 +3781,7 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto,
if (FD) {
FunctionTypeDepth.enterResultType();
mangleRequiresClause(FD->getTrailingRequiresClause());
mangleRequiresClause(FD->getTrailingRequiresClause().ConstraintExpr);
}
FunctionTypeDepth.pop(saved);

View File

@ -584,7 +584,8 @@ public:
if (LE->hasExplicitResultType())
TraverseTypeLoc(Proto.getReturnLoc());
TraverseStmt(LE->getTrailingRequiresClause());
TraverseStmt(
const_cast<Expr *>(LE->getTrailingRequiresClause().ConstraintExpr));
}
TraverseStmt(LE->getBody());

View File

@ -132,8 +132,8 @@ public:
}
}
}
if (auto *C = D->getTrailingRequiresClause())
IndexCtx.indexBody(C, Parent);
if (const AssociatedConstraint &C = D->getTrailingRequiresClause())
IndexCtx.indexBody(C.ConstraintExpr, Parent);
}
bool handleObjCMethod(const ObjCMethodDecl *D,

View File

@ -848,10 +848,8 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
ForOverloadResolution);
return CheckConstraintSatisfaction(
FD,
AssociatedConstraint(FD->getTrailingRequiresClause(),
ArgumentPackSubstitutionIndex),
*MLTAL, SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
FD, FD->getTrailingRequiresClause(), *MLTAL,
SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()),
Satisfaction);
}

View File

@ -9353,7 +9353,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
SemaRef.Context, DC, D.getBeginLoc(), NameInfo, R, TInfo, SC,
SemaRef.getCurFPFeatures().isFPConstrained(), isInline, HasPrototype,
ConstexprSpecKind::Unspecified,
/*TrailingRequiresClause=*/nullptr);
/*TrailingRequiresClause=*/{});
if (D.isInvalidType())
NewFD->setInvalidDecl();
@ -9361,7 +9361,7 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
}
ExplicitSpecifier ExplicitSpecifier = D.getDeclSpec().getExplicitSpecifier();
Expr *TrailingRequiresClause = D.getTrailingRequiresClause();
AssociatedConstraint TrailingRequiresClause(D.getTrailingRequiresClause());
SemaRef.CheckExplicitObjectMemberFunction(DC, D, Name, R);
@ -10531,7 +10531,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
diag::ext_operator_new_delete_declared_inline)
<< NewFD->getDeclName();
if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
if (const Expr *TRC = NewFD->getTrailingRequiresClause().ConstraintExpr) {
// C++20 [dcl.decl.general]p4:
// The optional requires-clause in an init-declarator or
// member-declarator shall be present only if the declarator declares a
@ -12261,7 +12261,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
if (Method->isVirtual() && NewFD->getTrailingRequiresClause())
// C++2a [class.virtual]p6
// A virtual method shall not have a requires-clause.
Diag(NewFD->getTrailingRequiresClause()->getBeginLoc(),
Diag(NewFD->getTrailingRequiresClause().ConstraintExpr->getBeginLoc(),
diag::err_constrained_virtual_method);
if (Method->isStatic())
@ -19085,8 +19085,7 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record,
SmallVector<bool, 4> SatisfactionStatus;
for (CXXMethodDecl *Method : Methods) {
const Expr *Constraints = Method->getTrailingRequiresClause();
if (!Constraints)
if (!Method->getTrailingRequiresClause())
SatisfactionStatus.push_back(true);
else {
ConstraintSatisfaction Satisfaction;
@ -19105,7 +19104,7 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record,
if (FunctionDecl *MF = OrigMethod->getInstantiatedFromMemberFunction())
OrigMethod = cast<CXXMethodDecl>(MF);
const Expr *Constraints = OrigMethod->getTrailingRequiresClause();
AssociatedConstraint Orig = OrigMethod->getTrailingRequiresClause();
bool AnotherMethodIsMoreConstrained = false;
for (size_t j = 0; j < Methods.size(); j++) {
if (i == j || !SatisfactionStatus[j])
@ -19118,15 +19117,13 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record,
CSM))
continue;
const Expr *OtherConstraints = OtherMethod->getTrailingRequiresClause();
if (!OtherConstraints)
AssociatedConstraint Other = OtherMethod->getTrailingRequiresClause();
if (!Other)
continue;
if (!Constraints) {
if (!Orig) {
AnotherMethodIsMoreConstrained = true;
break;
}
AssociatedConstraint Other(OtherConstraints);
AssociatedConstraint Orig(Constraints);
if (S.IsAtLeastAsConstrained(OtherMethod, {Other}, OrigMethod, {Orig},
AnotherMethodIsMoreConstrained)) {
// There was an error with the constraints comparison. Exit the loop

View File

@ -18995,8 +18995,8 @@ bool Sema::checkThisInStaticMemberFunctionType(CXXMethodDecl *Method) {
return true;
// Check the trailing requires clause
if (Expr *E = Method->getTrailingRequiresClause())
if (!Finder.TraverseStmt(E))
if (const AssociatedConstraint &TRC = Method->getTrailingRequiresClause())
if (!Finder.TraverseStmt(const_cast<Expr *>(TRC.ConstraintExpr)))
return true;
return checkThisInStaticMemberFunctionAttributes(Method);

View File

@ -990,7 +990,7 @@ private:
followDestructor(dyn_cast<CXXRecordDecl>(Dtor->getParent()), Dtor);
if (auto *FD = dyn_cast<FunctionDecl>(CurrentCaller.CDecl)) {
TrailingRequiresClause = FD->getTrailingRequiresClause();
TrailingRequiresClause = FD->getTrailingRequiresClause().ConstraintExpr;
// Note that FD->getType->getAs<FunctionProtoType>() can yield a
// noexcept Expr which has been boiled down to a constant expression.

View File

@ -1015,7 +1015,7 @@ CXXMethodDecl *Sema::CreateLambdaCallOperator(SourceRange IntroducerRange,
QualType(), /*Tinfo=*/nullptr, SC_None,
getCurFPFeatures().isFPConstrained(),
/*isInline=*/true, ConstexprSpecKind::Unspecified, SourceLocation(),
/*TrailingRequiresClause=*/nullptr);
/*TrailingRequiresClause=*/{});
Method->setAccess(AS_public);
return Method;
}
@ -1033,7 +1033,8 @@ void Sema::AddTemplateParametersToLambdaCallOperator(
void Sema::CompleteLambdaCallOperator(
CXXMethodDecl *Method, SourceLocation LambdaLoc,
SourceLocation CallOperatorLoc, Expr *TrailingRequiresClause,
SourceLocation CallOperatorLoc,
const AssociatedConstraint &TrailingRequiresClause,
TypeSourceInfo *MethodTyInfo, ConstexprSpecKind ConstexprKind,
StorageClass SC, ArrayRef<ParmVarDecl *> Params,
bool HasExplicitResultType) {
@ -1461,8 +1462,9 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
CompleteLambdaCallOperator(
Method, Intro.Range.getBegin(), CallOperatorLoc,
ParamInfo.getTrailingRequiresClause(), MethodTyInfo,
ParamInfo.getDeclSpec().getConstexprSpecifier(),
AssociatedConstraint(ParamInfo.getTrailingRequiresClause(),
/*ArgumentPackSubstitutionIndex=*/-1),
MethodTyInfo, ParamInfo.getDeclSpec().getConstexprSpecifier(),
IsLambdaStatic ? SC_Static : SC_None, Params, ExplicitResultType);
CheckCXXDefaultArguments(Method);
@ -1545,7 +1547,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
// The optional requires-clause ([temp.pre]) in an init-declarator or
// member-declarator shall be present only if the declarator declares a
// templated function ([dcl.fct]).
if (Expr *TRC = Method->getTrailingRequiresClause()) {
if (const AssociatedConstraint &TRC = Method->getTrailingRequiresClause()) {
// [temp.pre]/8:
// An entity is templated if it is
// - a template,
@ -1568,7 +1570,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
// applies to the call operator, which we already know is a member function,
// AND defined.
if (!Method->getDescribedFunctionTemplate() && !Method->isTemplated()) {
Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
Diag(TRC.ConstraintExpr->getBeginLoc(),
diag::err_constrained_non_templated_function);
}
}
@ -1791,7 +1794,8 @@ static void addFunctionPointerConversion(Sema &S, SourceRange IntroducerRange,
// A non-generic lambda may still be a templated entity. We need to preserve
// constraints when converting the lambda to a function pointer. See GH63181.
if (Expr *Requires = CallOperator->getTrailingRequiresClause())
if (const AssociatedConstraint &Requires =
CallOperator->getTrailingRequiresClause())
Conversion->setTrailingRequiresClause(Requires);
if (Class->isGenericLambda()) {

View File

@ -1551,12 +1551,16 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
if (!UseOverrideRules &&
New->getTemplateSpecializationKind() != TSK_ExplicitSpecialization) {
Expr *NewRC = New->getTrailingRequiresClause(),
*OldRC = Old->getTrailingRequiresClause();
if ((NewRC != nullptr) != (OldRC != nullptr))
AssociatedConstraint NewRC = New->getTrailingRequiresClause(),
OldRC = Old->getTrailingRequiresClause();
if (!NewRC != !OldRC)
return true;
if (NewRC.ArgumentPackSubstitutionIndex !=
OldRC.ArgumentPackSubstitutionIndex)
return true;
if (NewRC &&
!SemaRef.AreConstraintExpressionsEqual(OldDecl, OldRC, NewDecl, NewRC))
!SemaRef.AreConstraintExpressionsEqual(OldDecl, OldRC.ConstraintExpr,
NewDecl, NewRC.ConstraintExpr))
return true;
}

View File

@ -200,7 +200,7 @@ buildDeductionGuide(Sema &SemaRef, TemplateDecl *OriginalTemplate,
TypeSourceInfo *TInfo, SourceLocation LocStart,
SourceLocation Loc, SourceLocation LocEnd, bool IsImplicit,
llvm::ArrayRef<TypedefNameDecl *> MaterializedTypedefs = {},
Expr *FunctionTrailingRC = nullptr) {
const AssociatedConstraint &FunctionTrailingRC = {}) {
DeclContext *DC = OriginalTemplate->getDeclContext();
auto DeductionGuideName =
SemaRef.Context.DeclarationNames.getCXXDeductionGuideName(
@ -356,7 +356,8 @@ struct ConvertConstructorToDeductionGuideTransform {
TemplateParameterList *TemplateParams =
SemaRef.GetTemplateParameterList(Template);
SmallVector<TemplateArgument, 16> Depth1Args;
Expr *OuterRC = TemplateParams->getRequiresClause();
AssociatedConstraint OuterRC(TemplateParams->getRequiresClause(),
/*ArgumentPackSubstitutionIndex=*/-1);
if (FTD) {
TemplateParameterList *InnerParams = FTD->getTemplateParameters();
SmallVector<NamedDecl *, 16> AllParams;
@ -456,18 +457,20 @@ struct ConvertConstructorToDeductionGuideTransform {
// At this point, the function parameters are already 'instantiated' in the
// current scope. Substitute into the constructor's trailing
// requires-clause, if any.
Expr *FunctionTrailingRC = nullptr;
if (Expr *RC = CD->getTrailingRequiresClause()) {
AssociatedConstraint FunctionTrailingRC;
if (const AssociatedConstraint &RC = CD->getTrailingRequiresClause()) {
MultiLevelTemplateArgumentList Args;
Args.setKind(TemplateSubstitutionKind::Rewrite);
Args.addOuterTemplateArguments(Depth1Args);
Args.addOuterRetainedLevel();
if (NestedPattern)
Args.addOuterRetainedLevels(NestedPattern->getTemplateDepth());
ExprResult E = SemaRef.SubstConstraintExprWithoutSatisfaction(RC, Args);
ExprResult E = SemaRef.SubstConstraintExprWithoutSatisfaction(
const_cast<Expr *>(RC.ConstraintExpr), Args);
if (!E.isUsable())
return nullptr;
FunctionTrailingRC = E.get();
FunctionTrailingRC =
AssociatedConstraint(E.get(), RC.ArgumentPackSubstitutionIndex);
}
// C++ [over.match.class.deduct]p1:
@ -480,13 +483,19 @@ struct ConvertConstructorToDeductionGuideTransform {
if (OuterRC) {
// The outer template parameters are not transformed, so their
// associated constraints don't need substitution.
// FIXME: Should simply add another field for the OuterRC, instead of
// combining them like this.
if (!FunctionTrailingRC)
FunctionTrailingRC = OuterRC;
else
FunctionTrailingRC = BinaryOperator::Create(
SemaRef.Context, /*lhs=*/OuterRC, /*rhs=*/FunctionTrailingRC,
FunctionTrailingRC = AssociatedConstraint(
BinaryOperator::Create(
SemaRef.Context,
/*lhs=*/const_cast<Expr *>(OuterRC.ConstraintExpr),
/*rhs=*/const_cast<Expr *>(FunctionTrailingRC.ConstraintExpr),
BO_LAnd, SemaRef.Context.BoolTy, VK_PRValue, OK_Ordinary,
TemplateParams->getTemplateLoc(), FPOptionsOverride());
TemplateParams->getTemplateLoc(), FPOptionsOverride()),
FunctionTrailingRC.ArgumentPackSubstitutionIndex);
}
return buildDeductionGuide(
@ -1238,14 +1247,20 @@ void DeclareImplicitDeductionGuidesForTypeAlias(
// FIXME: Here the synthesized deduction guide is not a templated
// function. Per [dcl.decl]p4, the requires-clause shall be present only
// if the declarator declares a templated function, a bug in standard?
auto *Constraint = buildIsDeducibleConstraint(
SemaRef, AliasTemplate, Transformed->getReturnType(), {});
if (auto *RC = DG->getTrailingRequiresClause()) {
auto Conjunction =
SemaRef.BuildBinOp(SemaRef.getCurScope(), SourceLocation{},
BinaryOperatorKind::BO_LAnd, RC, Constraint);
if (!Conjunction.isInvalid())
Constraint = Conjunction.getAs<Expr>();
AssociatedConstraint Constraint(
buildIsDeducibleConstraint(SemaRef, AliasTemplate,
Transformed->getReturnType(), {}),
/*ArgumentPackSubstitutionIndex=*/-1);
if (const AssociatedConstraint &RC = DG->getTrailingRequiresClause()) {
auto Conjunction = SemaRef.BuildBinOp(
SemaRef.getCurScope(), SourceLocation{},
BinaryOperatorKind::BO_LAnd, const_cast<Expr *>(RC.ConstraintExpr),
const_cast<Expr *>(Constraint.ConstraintExpr));
if (!Conjunction.isInvalid()) {
Constraint.ConstraintExpr = Conjunction.getAs<Expr>();
Constraint.ArgumentPackSubstitutionIndex =
RC.ArgumentPackSubstitutionIndex;
}
}
Transformed->setTrailingRequiresClause(Constraint);
continue;

View File

@ -2671,7 +2671,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
return nullptr;
}
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
AssociatedConstraint TrailingRequiresClause = D->getTrailingRequiresClause();
// If we're instantiating a local function declaration, put the result
// in the enclosing namespace; otherwise we need to find the instantiated
@ -3102,7 +3102,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
}
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
AssociatedConstraint TrailingRequiresClause = D->getTrailingRequiresClause();
DeclarationNameInfo NameInfo
= SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs);

View File

@ -15659,10 +15659,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
auto FPTL = NewCallOpTSI->getTypeLoc().getAsAdjusted<FunctionProtoTypeLoc>();
assert(FPTL && "Not a FunctionProtoType?");
AssociatedConstraint TRC = E->getCallOperator()->getTrailingRequiresClause();
if (TRC.ArgumentPackSubstitutionIndex == -1)
TRC.ArgumentPackSubstitutionIndex = SemaRef.ArgumentPackSubstitutionIndex;
getSema().CompleteLambdaCallOperator(
NewCallOperator, E->getCallOperator()->getLocation(),
E->getCallOperator()->getInnerLocStart(),
E->getCallOperator()->getTrailingRequiresClause(), NewCallOpTSI,
E->getCallOperator()->getInnerLocStart(), TRC, NewCallOpTSI,
E->getCallOperator()->getConstexprKind(),
E->getCallOperator()->getStorageClass(), FPTL.getParams(),
E->hasExplicitResultType());

View File

@ -904,7 +904,8 @@ void ASTDeclReader::VisitDeclaratorDecl(DeclaratorDecl *DD) {
if (Record.readInt()) { // hasExtInfo
auto *Info = new (Reader.getContext()) DeclaratorDecl::ExtInfo();
Record.readQualifierInfo(*Info);
Info->TrailingRequiresClause = Record.readExpr();
Info->TrailingRequiresClause =
AssociatedConstraint(Record.readExpr(), int(Record.readInt()));
DD->DeclInfo = Info;
}
QualType TSIType = Record.readType();

View File

@ -728,7 +728,10 @@ void ASTDeclWriter::VisitDeclaratorDecl(DeclaratorDecl *D) {
if (D->hasExtInfo()) {
DeclaratorDecl::ExtInfo *Info = D->getExtInfo();
Record.AddQualifierInfo(*Info);
Record.AddStmt(Info->TrailingRequiresClause);
Record.AddStmt(
const_cast<Expr *>(Info->TrailingRequiresClause.ConstraintExpr));
Record.push_back(
Info->TrailingRequiresClause.ArgumentPackSubstitutionIndex);
}
// The location information is deferred until the end of the record.
Record.AddTypeRef(D->getTypeSourceInfo() ? D->getTypeSourceInfo()->getType()

View File

@ -267,6 +267,15 @@ static_assert(bazz<1, 2>()(1));
// expected-error@-1 {{is ambiguous}}
// expected-note@#bazz 2{{candidate function [with value:auto = int]}}
template <class T> concept C2 = sizeof(T) >= sizeof(int);
template <class... Ts> static constexpr auto trailing() {
return Overloaded{[](auto) requires (C2<Ts> && C2<int>) { return 0; }...}; // #trailing
}
static_assert(trailing<int, long>()(0));
// expected-error@-1 {{is ambiguous}}
// expected-note@#trailing 2{{candidate function [with auto:1 = int]}}
} // namespace GH101754
namespace GH131798 {

View File

@ -872,7 +872,7 @@ bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) {
// FIXME: Attributes?
}
if (auto *E = ND->getTrailingRequiresClause()) {
if (auto *E = ND->getTrailingRequiresClause().ConstraintExpr) {
if (Visit(E))
return true;
}