diff options
author | Matheus Izvekov <mizvekov@gmail.com> | 2025-04-01 21:11:56 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-01 21:11:56 -0300 |
commit | ad1ca5f4a2bc09f99fd82e5444f5da37c2985e97 (patch) | |
tree | 9d393f3b37d398a8c3bdcc52f68bbeed93773cfe /clang/lib/Sema/SemaConcept.cpp | |
parent | 2f25345670081f1ca460ea3f42a0585ef3f1e877 (diff) | |
download | llvm-ad1ca5f4a2bc09f99fd82e5444f5da37c2985e97.zip llvm-ad1ca5f4a2bc09f99fd82e5444f5da37c2985e97.tar.gz llvm-ad1ca5f4a2bc09f99fd82e5444f5da37c2985e97.tar.bz2 |
[clang] Concepts: support pack expansions for type constraints (#132626)
This reverts an earlier attempt
(adb0d8ddceb143749c519d14b8b31b481071da77 and
50e5411e4247421fd606f0a206682fcdf0303ae3) to support these expansions,
which was limited to type arguments and which subverted the purpose
of SubstTemplateTypeParmType.
This propagates the ArgumentPackSubstitutionIndex along with the
AssociatedConstraint, so that the pack expansion works, without
needing any new transforms or otherwise any changes to the template
instantiation process.
This keeps the tests from the reverted commits, and adds a few more
showing the new solution also works for NTTPs.
Fixes https://github.com/llvm/llvm-project/issues/131798
Diffstat (limited to 'clang/lib/Sema/SemaConcept.cpp')
-rw-r--r-- | clang/lib/Sema/SemaConcept.cpp | 94 |
1 files changed, 50 insertions, 44 deletions
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index ebee599..e6117f9 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -567,11 +567,12 @@ static ExprResult calculateConstraintSatisfaction( } static bool CheckConstraintSatisfaction( - Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, + Sema &S, const NamedDecl *Template, + ArrayRef<AssociatedConstraint> AssociatedConstraints, llvm::SmallVectorImpl<Expr *> &Converted, const MultiLevelTemplateArgumentList &TemplateArgsLists, SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { - if (ConstraintExprs.empty()) { + if (AssociatedConstraints.empty()) { Satisfaction.IsSatisfied = true; return false; } @@ -592,10 +593,12 @@ static bool CheckConstraintSatisfaction( if (Inst.isInvalid()) return true; - for (const Expr *ConstraintExpr : ConstraintExprs) { + for (const AssociatedConstraint &AC : AssociatedConstraints) { + Sema::ArgumentPackSubstitutionIndexRAII _(S, + AC.ArgumentPackSubstitutionIndex); ExprResult Res = calculateConstraintSatisfaction( S, Template, TemplateIDRange.getBegin(), TemplateArgsLists, - ConstraintExpr, Satisfaction); + AC.ConstraintExpr, Satisfaction); if (Res.isInvalid()) return true; @@ -603,7 +606,8 @@ static bool CheckConstraintSatisfaction( if (!Satisfaction.IsSatisfied) { // Backfill the 'converted' list with nulls so we can keep the Converted // and unconverted lists in sync. - Converted.append(ConstraintExprs.size() - Converted.size(), nullptr); + Converted.append(AssociatedConstraints.size() - Converted.size(), + nullptr); // [temp.constr.op] p2 // [...] To determine if a conjunction is satisfied, the satisfaction // of the first operand is checked. If that is not satisfied, the @@ -615,17 +619,18 @@ static bool CheckConstraintSatisfaction( } bool Sema::CheckConstraintSatisfaction( - const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, + const NamedDecl *Template, + ArrayRef<AssociatedConstraint> AssociatedConstraints, llvm::SmallVectorImpl<Expr *> &ConvertedConstraints, const MultiLevelTemplateArgumentList &TemplateArgsLists, SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { - if (ConstraintExprs.empty()) { + if (AssociatedConstraints.empty()) { OutSatisfaction.IsSatisfied = true; return false; } if (!Template) { return ::CheckConstraintSatisfaction( - *this, nullptr, ConstraintExprs, ConvertedConstraints, + *this, nullptr, AssociatedConstraints, ConvertedConstraints, TemplateArgsLists, TemplateIDRange, OutSatisfaction); } // Invalid templates could make their way here. Substituting them could result @@ -654,7 +659,7 @@ bool Sema::CheckConstraintSatisfaction( auto Satisfaction = std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs); - if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, + if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints, ConvertedConstraints, TemplateArgsLists, TemplateIDRange, *Satisfaction)) { OutSatisfaction = *Satisfaction; @@ -923,8 +928,10 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, ForOverloadResolution); return CheckConstraintSatisfaction( - FD, {FD->getTrailingRequiresClause()}, *MLTAL, - SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), + FD, + AssociatedConstraint(FD->getTrailingRequiresClause(), + ArgumentPackSubstitutionIndex), + *MLTAL, SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), Satisfaction); } @@ -1099,13 +1106,13 @@ bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) { assert(FD->getDescribedFunctionTemplate() && "Non-function templates don't need to be checked"); - SmallVector<const Expr *, 3> ACs; + SmallVector<AssociatedConstraint, 3> ACs; FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs); unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD); - for (const Expr *Constraint : ACs) + for (const AssociatedConstraint &AC : ACs) if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth, - Constraint)) + AC.ConstraintExpr)) return true; return false; @@ -1115,7 +1122,7 @@ bool Sema::EnsureTemplateArgumentListConstraints( TemplateDecl *TD, const MultiLevelTemplateArgumentList &TemplateArgsLists, SourceRange TemplateIDRange) { ConstraintSatisfaction Satisfaction; - llvm::SmallVector<const Expr *, 3> AssociatedConstraints; + llvm::SmallVector<AssociatedConstraint, 3> AssociatedConstraints; TD->getAssociatedConstraints(AssociatedConstraints); if (CheckConstraintSatisfaction(TD, AssociatedConstraints, TemplateArgsLists, TemplateIDRange, Satisfaction)) @@ -1146,7 +1153,7 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( FunctionTemplateDecl *Template = Decl->getPrimaryTemplate(); // Note - code synthesis context for the constraints check is created // inside CheckConstraintsSatisfaction. - SmallVector<const Expr *, 3> TemplateAC; + SmallVector<AssociatedConstraint, 3> TemplateAC; Template->getAssociatedConstraints(TemplateAC); if (TemplateAC.empty()) { Satisfaction.IsSatisfied = true; @@ -1438,7 +1445,7 @@ void Sema::DiagnoseUnsatisfiedConstraint( const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints( const NamedDecl *ConstrainedDecl, - ArrayRef<const Expr *> AssociatedConstraints) { + ArrayRef<AssociatedConstraint> AssociatedConstraints) { // In case the ConstrainedDecl comes from modules, it is necessary to use // the canonical decl to avoid different atomic constraints with the 'same' // declarations. @@ -1446,9 +1453,8 @@ const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints( auto CacheEntry = NormalizationCache.find(ConstrainedDecl); if (CacheEntry == NormalizationCache.end()) { - auto Normalized = - NormalizedConstraint::fromConstraintExprs(*this, ConstrainedDecl, - AssociatedConstraints); + auto Normalized = NormalizedConstraint::fromAssociatedConstraints( + *this, ConstrainedDecl, AssociatedConstraints); CacheEntry = NormalizationCache .try_emplace(ConstrainedDecl, @@ -1463,7 +1469,7 @@ const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints( const NormalizedConstraint *clang::getNormalizedAssociatedConstraints( Sema &S, const NamedDecl *ConstrainedDecl, - ArrayRef<const Expr *> AssociatedConstraints) { + ArrayRef<AssociatedConstraint> AssociatedConstraints) { return S.getNormalizedAssociatedConstraints(ConstrainedDecl, AssociatedConstraints); } @@ -1593,14 +1599,14 @@ NormalizedConstraint &NormalizedConstraint::getRHS() const { } std::optional<NormalizedConstraint> -NormalizedConstraint::fromConstraintExprs(Sema &S, const NamedDecl *D, - ArrayRef<const Expr *> E) { - assert(E.size() != 0); - auto Conjunction = fromConstraintExpr(S, D, E[0]); +NormalizedConstraint::fromAssociatedConstraints( + Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) { + assert(ACs.size() != 0); + auto Conjunction = fromConstraintExpr(S, D, ACs[0].ConstraintExpr); if (!Conjunction) return std::nullopt; - for (unsigned I = 1; I < E.size(); ++I) { - auto Next = fromConstraintExpr(S, D, E[I]); + for (unsigned I = 1; I < ACs.size(); ++I) { + auto Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr); if (!Next) return std::nullopt; *Conjunction = NormalizedConstraint(S.Context, std::move(*Conjunction), @@ -1655,8 +1661,8 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, // expression, the program is ill-formed; no diagnostic is required. // [...] ConceptDecl *CD = CSE->getNamedConcept(); - SubNF = S.getNormalizedAssociatedConstraints(CD, - {CD->getConstraintExpr()}); + SubNF = S.getNormalizedAssociatedConstraints( + CD, AssociatedConstraint(CD->getConstraintExpr())); if (!SubNF) return std::nullopt; } @@ -1731,9 +1737,9 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption( } bool Sema::IsAtLeastAsConstrained(const NamedDecl *D1, - MutableArrayRef<const Expr *> AC1, + MutableArrayRef<AssociatedConstraint> AC1, const NamedDecl *D2, - MutableArrayRef<const Expr *> AC2, + MutableArrayRef<AssociatedConstraint> AC2, bool &Result) { #ifndef NDEBUG if (const auto *FD1 = dyn_cast<FunctionDecl>(D1)) { @@ -1771,13 +1777,15 @@ bool Sema::IsAtLeastAsConstrained(const NamedDecl *D1, for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) { if (Depth2 > Depth1) { - AC1[I] = AdjustConstraintDepth(*this, Depth2 - Depth1) - .TransformExpr(const_cast<Expr *>(AC1[I])) - .get(); + AC1[I].ConstraintExpr = + AdjustConstraintDepth(*this, Depth2 - Depth1) + .TransformExpr(const_cast<Expr *>(AC1[I].ConstraintExpr)) + .get(); } else if (Depth1 > Depth2) { - AC2[I] = AdjustConstraintDepth(*this, Depth1 - Depth2) - .TransformExpr(const_cast<Expr *>(AC2[I])) - .get(); + AC2[I].ConstraintExpr = + AdjustConstraintDepth(*this, Depth1 - Depth2) + .TransformExpr(const_cast<Expr *>(AC2[I].ConstraintExpr)) + .get(); } } @@ -1793,9 +1801,8 @@ bool Sema::IsAtLeastAsConstrained(const NamedDecl *D1, } bool Sema::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) { if (isSFINAEContext()) // No need to work here because our notes would be discarded. return false; @@ -2106,10 +2113,9 @@ void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) { F.push_back(C); } -std::optional<bool> SubsumptionChecker::Subsumes(const NamedDecl *DP, - ArrayRef<const Expr *> P, - const NamedDecl *DQ, - ArrayRef<const Expr *> Q) { +std::optional<bool> SubsumptionChecker::Subsumes( + const NamedDecl *DP, ArrayRef<AssociatedConstraint> P, const NamedDecl *DQ, + ArrayRef<AssociatedConstraint> Q) { const NormalizedConstraint *PNormalized = getNormalizedAssociatedConstraints(SemaRef, DP, P); if (!PNormalized) |