[clang] [Clang][WIP] Normalize constraints before checking for satisfaction (PR #141776)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 30 05:46:41 PDT 2025
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/141776
>From 7a832e48d4750a248c0fc4ac684f5e6f156a7dec Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 28 May 2025 16:15:22 +0200
Subject: [PATCH 1/2] [Clang][WIP] Normalize constraints before checking for
satisfaction
In the standard, constraint satisfaction checking is done on the normalized
form of a constraint.
Clang instead substitute on the non-normalized form, which cause us
to report substitution failures in template arguments or concept ids,
which is non-conforming but unavoidable witjout a parameter mapping
This patch normalizes before satisfaction checking.
However, we preserve concept-id nodes in the normalized form,
solely for diagnostics purposes.
This is a very incomplete attempt at addressing #61811
and related concepts related conformance bugs, ideally
to make the implementation of concept template parameters easier
There is stil ~20 failing test files, mostly caused by poor
adjustement of the template depth.
---
clang/include/clang/AST/ASTConcept.h | 6 +-
clang/include/clang/AST/TemplateBase.h | 1 -
clang/include/clang/Sema/Sema.h | 65 +-
clang/include/clang/Sema/SemaConcept.h | 379 ++++--
clang/include/clang/Sema/Template.h | 21 +-
clang/lib/AST/ASTConcept.cpp | 11 +-
clang/lib/AST/ASTContext.cpp | 1 +
clang/lib/Sema/SemaConcept.cpp | 1171 +++++++++--------
clang/lib/Sema/SemaDeclCXX.cpp | 16 +-
clang/lib/Sema/SemaExprCXX.cpp | 11 +-
clang/lib/Sema/SemaTemplate.cpp | 37 +-
clang/lib/Sema/SemaTemplateDeduction.cpp | 8 +
clang/lib/Sema/SemaTemplateInstantiate.cpp | 1051 ++++++++-------
clang/lib/Sema/SemaTemplateVariadic.cpp | 2 +
clang/lib/Sema/TreeTransform.h | 42 +-
clang/lib/Serialization/ASTWriterStmt.cpp | 5 +-
clang/test/CXX/drs/cwg25xx.cpp | 12 +-
.../CXX/expr/expr.prim/expr.prim.id/p3.cpp | 4 +-
.../constrant-satisfaction-conversions.cpp | 3 +-
.../temp.constr/temp.constr.normal/p1.cpp | 10 +-
clang/test/SemaCXX/cxx23-assume.cpp | 10 +-
.../SemaTemplate/concepts-recovery-expr.cpp | 32 +-
.../instantiate-expanded-type-constraint.cpp | 1 -
23 files changed, 1669 insertions(+), 1230 deletions(-)
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index c8f6330a73bb1..c98dd4629abfc 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -27,6 +27,7 @@
namespace clang {
class ConceptDecl;
+class ConceptReference;
class Expr;
class NamedDecl;
struct PrintingPolicy;
@@ -34,6 +35,7 @@ struct PrintingPolicy;
/// The result of a constraint satisfaction check, containing the necessary
/// information to diagnose an unsatisfied constraint.
class ConstraintSatisfaction : public llvm::FoldingSetNode {
+private:
// The template-like entity that 'owns' the constraint checked here (can be a
// constrained entity or a concept).
const NamedDecl *ConstraintOwner = nullptr;
@@ -48,7 +50,7 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
: ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {}
using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
- using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;
+ using Detail = llvm::PointerUnion<const Expr *, SubstitutionDiagnostic *>;
bool IsSatisfied = false;
bool ContainsErrors = false;
@@ -79,7 +81,7 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
/// substituted into them, or a diagnostic if substitution resulted in
/// an invalid expression.
using UnsatisfiedConstraintRecord =
- llvm::PointerUnion<Expr *, std::pair<SourceLocation, StringRef> *>;
+ llvm::PointerUnion<const Expr *, std::pair<SourceLocation, StringRef> *>;
/// \brief The result of a constraint satisfaction check, containing the
/// necessary information to diagnose an unsatisfied constraint.
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index 279feb858e665..8b658ac9db503 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -473,7 +473,6 @@ class TemplateArgument {
/// Used to insert TemplateArguments into FoldingSets.
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) const;
};
-
/// Location information for a TemplateArgument.
struct TemplateArgumentLocInfo {
private:
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bbc5c181c6a10..b1f19ef6c067c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11703,7 +11703,8 @@ class Sema final : public SemaBase {
CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
- const TemplateArgumentListInfo *TemplateArgs);
+ const TemplateArgumentListInfo *TemplateArgs,
+ bool DoCheckConstraintSatisfaction = true);
void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc);
void diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
@@ -12777,6 +12778,9 @@ class Sema final : public SemaBase {
void MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
unsigned Depth, llvm::SmallBitVector &Used);
+ void MarkUsedTemplateParameters(ArrayRef<TemplateArgumentLoc> TemplateArgs,
+ unsigned Depth, llvm::SmallBitVector &Used);
+
void
MarkDeducedTemplateParameters(const FunctionTemplateDecl *FunctionTemplate,
llvm::SmallBitVector &Deduced) {
@@ -13336,6 +13340,11 @@ class Sema final : public SemaBase {
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Outputs);
+ bool SubstTemplateArgumentsInParameterMapping(
+ ArrayRef<TemplateArgumentLoc> Args,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ TemplateArgumentListInfo &Out);
+
/// Retrieve the template argument list(s) that should be used to
/// instantiate the definition of the given declaration.
///
@@ -14666,6 +14675,10 @@ class Sema final : public SemaBase {
SatisfactionStack.swap(NewSS);
}
+ using ConstrainedDeclOrNestedRequirement =
+ llvm::PointerUnion<const NamedDecl *,
+ const concepts::NestedRequirement *>;
+
/// Check whether the given expression is a valid constraint expression.
/// A diagnostic is emitted if it is not, false is returned, and
/// PossibleNonPrimary will be set to true if the failure might be due to a
@@ -14690,44 +14703,11 @@ class Sema final : public SemaBase {
/// \returns true if an error occurred and satisfaction could not be checked,
/// false otherwise.
bool CheckConstraintSatisfaction(
- const NamedDecl *Template,
+ ConstrainedDeclOrNestedRequirement Entity,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgLists,
- SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
- llvm::SmallVector<Expr *, 4> Converted;
- return CheckConstraintSatisfaction(Template, AssociatedConstraints,
- Converted, TemplateArgLists,
- TemplateIDRange, Satisfaction);
- }
-
- /// \brief Check whether the given list of constraint expressions are
- /// satisfied (as if in a 'conjunction') given template arguments.
- /// Additionally, takes an empty list of Expressions which is populated with
- /// the instantiated versions of the ConstraintExprs.
- /// \param Template the template-like entity that triggered the constraints
- /// check (either a concept or a constrained entity).
- /// \param ConstraintExprs a list of constraint expressions, treated as if
- /// they were 'AND'ed together.
- /// \param ConvertedConstraints a out parameter that will get populated with
- /// the instantiated version of the ConstraintExprs if we successfully checked
- /// satisfaction.
- /// \param TemplateArgList the multi-level list of template arguments to
- /// substitute into the constraint expression. This should be relative to the
- /// top-level (hence multi-level), since we need to instantiate fully at the
- /// time of checking.
- /// \param TemplateIDRange The source range of the template id that
- /// caused the constraints check.
- /// \param Satisfaction if true is returned, will contain details of the
- /// satisfaction, with enough information to diagnose an unsatisfied
- /// expression.
- /// \returns true if an error occurred and satisfaction could not be checked,
- /// false otherwise.
- bool CheckConstraintSatisfaction(
- const NamedDecl *Template,
- ArrayRef<AssociatedConstraint> AssociatedConstraints,
- llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
- const MultiLevelTemplateArgumentList &TemplateArgList,
- SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
+ SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
+ const ConceptReference *TopLevelConceptId = nullptr);
/// \brief Check whether the given non-dependent constraint expression is
/// satisfied. Returns false and updates Satisfaction with the satisfaction
@@ -14793,16 +14773,16 @@ class Sema final : public SemaBase {
/// \param First whether this is the first time an unsatisfied constraint is
/// diagnosed for this error.
void DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction,
+ SourceLocation Loc = {},
bool First = true);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
- void
- DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction,
- bool First = true);
+ void DiagnoseUnsatisfiedConstraint(
+ const ConceptSpecializationExpr *ConstraintExpr);
const NormalizedConstraint *getNormalizedAssociatedConstraints(
- const NamedDecl *ConstrainedDecl,
+ ConstrainedDeclOrNestedRequirement Entity,
ArrayRef<AssociatedConstraint> AssociatedConstraints);
/// \brief Check whether the given declaration's associated constraints are
@@ -14837,7 +14817,8 @@ class Sema final : public SemaBase {
/// constrained declarations). If an error occurred while normalizing the
/// associated constraints of the template or concept, nullptr will be cached
/// here.
- llvm::DenseMap<const NamedDecl *, NormalizedConstraint *> NormalizationCache;
+ llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint *>
+ NormalizationCache;
llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &>
SatisfactionCache;
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index 648a9c51ae6c1..0a79a6b5cc43f 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -16,130 +16,355 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/ExprConcepts.h"
#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/UnsignedOrNone.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Compiler.h"
#include <optional>
#include <utility>
namespace clang {
class Sema;
+class MultiLevelTemplateArgumentList;
-enum { ConstraintAlignment = 8 };
+/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
+/// either an atomic constraint, a conjunction of normalized constraints or a
+/// disjunction of normalized constraints.
+
+struct NormalizedConstraint {
+
+ enum class ConstraintKind {
+ Atomic = 0,
+ ConceptId,
+ FoldExpanded,
+ Compound,
+ };
+
+ enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
+ enum class FoldOperatorKind : unsigned int { And, Or };
+
+ using OccurenceList = llvm::SmallBitVector;
+
+ friend bool
+ substituteParameterMappings(Sema &S, NormalizedConstraint &N,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ TemplateParameterList *TemplateParams,
+ const NamedDecl *D);
+
+protected:
+ using ExprOrConcept =
+ llvm::PointerUnion<const Expr *, const ConceptReference *>;
+
+ struct AtomicBits {
+ LLVM_PREFERRED_TYPE(ConstraintKind)
+ unsigned Kind : 5;
+ unsigned : 1;
+ unsigned PackSubstitutionIndex : 26;
+ llvm::SmallBitVector Indexes;
+ TemplateArgumentLoc *Args;
+ ExprOrConcept ConstraintExpr;
+ const NamedDecl *ConstraintDecl;
+ };
-struct alignas(ConstraintAlignment) AtomicConstraint {
- const Expr *ConstraintExpr;
- const NamedDecl *ConstraintDecl;
- std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping;
+ struct FoldExpandedBits {
+ LLVM_PREFERRED_TYPE(ConstraintKind)
+ unsigned Kind : 5;
+ LLVM_PREFERRED_TYPE(FoldOperatorKind)
+ unsigned FoldOperator : 1;
+ unsigned : 26;
+ OccurenceList Indexes;
+ TemplateArgumentLoc *Args;
+ const Expr *Pattern;
+ NormalizedConstraint *Constraint;
+ };
- AtomicConstraint(const Expr *ConstraintExpr, const NamedDecl *ConstraintDecl)
- : ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {};
+ struct ConceptIdBits : AtomicBits {
+ NormalizedConstraint *Sub;
+ };
+
+ struct CompoundBits {
+ LLVM_PREFERRED_TYPE(ConstraintKind)
+ unsigned Kind : 5;
+ LLVM_PREFERRED_TYPE(CompoundConstraintKind)
+ unsigned CCK : 1;
+ NormalizedConstraint *LHS;
+ NormalizedConstraint *RHS;
+ };
+
+ union {
+ AtomicBits Atomic;
+ FoldExpandedBits FoldExpanded;
+ ConceptIdBits ConceptId;
+ CompoundBits Compound;
+ };
+
+ ~NormalizedConstraint() {
+ if (getKind() != ConstraintKind::Compound)
+ Atomic.Indexes.llvm::SmallBitVector::~SmallBitVector();
+ }
+
+ NormalizedConstraint(const Expr *ConstraintExpr,
+ const NamedDecl *ConstraintDecl,
+ UnsignedOrNone PackIndex)
+ : Atomic{llvm::to_underlying(ConstraintKind::Atomic),
+ PackIndex.toInternalRepresentation(),
+ /*Indexes=*/{},
+ /*Args=*/nullptr,
+ ConstraintExpr,
+ ConstraintDecl} {}
+
+ NormalizedConstraint(const Expr *Pattern, FoldOperatorKind OpKind,
+ NormalizedConstraint *Constraint)
+ : FoldExpanded{llvm::to_underlying(ConstraintKind::FoldExpanded),
+ llvm::to_underlying(OpKind),
+ /*Indexes=*/{},
+ /*Args=*/nullptr,
+ Pattern,
+ Constraint} {}
+
+ NormalizedConstraint(const ConceptReference *ConceptId,
+ const NamedDecl *ConstraintDecl,
+ NormalizedConstraint *SubConstraint,
+ UnsignedOrNone PackIndex)
+ : ConceptId{{llvm::to_underlying(ConstraintKind::ConceptId),
+ PackIndex.toInternalRepresentation(), /*Indexes=*/{},
+ /*Args=*/nullptr, ConceptId, ConstraintDecl},
+ SubConstraint} {}
+
+ NormalizedConstraint(NormalizedConstraint *LHS, CompoundConstraintKind CCK,
+ NormalizedConstraint *RHS)
+ : Compound{llvm::to_underlying(ConstraintKind::Compound), CCK, LHS, RHS} {
+ }
+
+ bool hasParameterMapping() const {
+ assert(getKind() != ConstraintKind::Compound);
+ return Atomic.Args != nullptr;
+ }
+
+ const OccurenceList &mappingOccurenceList() const {
+ assert(hasParameterMapping() && "This constraint has no parameter mapping");
+ return Atomic.Indexes;
+ }
+
+ llvm::MutableArrayRef<TemplateArgumentLoc> getParameterMapping() const {
+ return {Atomic.Args, Atomic.Indexes.count()};
+ }
+
+ void InitParameterMapping(TemplateParameterList *TemplateParams, const Expr *,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten);
+
+ void updateParameterMapping(OccurenceList Indexes,
+ llvm::MutableArrayRef<TemplateArgumentLoc> Args) {
+ assert(getKind() != ConstraintKind::Compound);
+ assert(Indexes.count() == Args.size());
+ Atomic.Indexes = Indexes;
+ Atomic.Args = Args.data();
+ }
bool hasMatchingParameterMapping(ASTContext &C,
- const AtomicConstraint &Other) const {
- if (!ParameterMapping != !Other.ParameterMapping)
+ const NormalizedConstraint &Other) const {
+ assert(getKind() != ConstraintKind::Compound);
+
+ if (hasParameterMapping() != Other.hasParameterMapping())
return false;
- if (!ParameterMapping)
+ if (!hasParameterMapping())
return true;
- if (ParameterMapping->size() != Other.ParameterMapping->size())
+
+ llvm::ArrayRef<TemplateArgumentLoc> ParameterMapping =
+ getParameterMapping();
+ llvm::ArrayRef<TemplateArgumentLoc> OtherParameterMapping =
+ Other.getParameterMapping();
+
+ if (ParameterMapping.size() != OtherParameterMapping.size())
return false;
- for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) {
+ for (unsigned I = 0, S = ParameterMapping.size(); I < S; ++I) {
llvm::FoldingSetNodeID IDA, IDB;
- C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
+ C.getCanonicalTemplateArgument(ParameterMapping[I].getArgument())
.Profile(IDA, C);
- C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument())
+ C.getCanonicalTemplateArgument(OtherParameterMapping[I].getArgument())
.Profile(IDB, C);
if (IDA != IDB)
return false;
}
return true;
}
-};
-struct alignas(ConstraintAlignment) NormalizedConstraintPair;
-struct alignas(ConstraintAlignment) FoldExpandedConstraint;
+public:
+ ConstraintKind getKind() const {
+ return static_cast<ConstraintKind>(Atomic.Kind);
+ }
+
+ SourceLocation getBeginLoc() const {
+ switch (getKind()) {
+ case ConstraintKind::Atomic:
+ return cast<const Expr *>(Atomic.ConstraintExpr)->getBeginLoc();
+ case ConstraintKind::ConceptId:
+ return cast<const ConceptReference *>(Atomic.ConstraintExpr)
+ ->getBeginLoc();
+ case ConstraintKind::Compound:
+ return Compound.LHS->getBeginLoc();
+ case ConstraintKind::FoldExpanded:
+ return FoldExpanded.Pattern->getBeginLoc();
+ }
+ }
-/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
-/// either an atomic constraint, a conjunction of normalized constraints or a
-/// disjunction of normalized constraints.
-struct NormalizedConstraint {
+ SourceLocation getEndLoc() const {
+ switch (getKind()) {
+ case ConstraintKind::Atomic:
+ return cast<const Expr *>(Atomic.ConstraintExpr)->getEndLoc();
+ case ConstraintKind::ConceptId:
+ return cast<const ConceptReference *>(Atomic.ConstraintExpr)->getEndLoc();
+ case ConstraintKind::Compound:
+ return Compound.RHS->getEndLoc();
+ case ConstraintKind::FoldExpanded:
+ return FoldExpanded.Pattern->getEndLoc();
+ }
+ }
+
+ SourceRange getSourceRange() const { return {getBeginLoc(), getEndLoc()}; }
+
+private:
friend class Sema;
+ static NormalizedConstraint *
+ fromAssociatedConstraints(Sema &S, const NamedDecl *D,
+ ArrayRef<AssociatedConstraint> ACs);
+ static NormalizedConstraint *fromConstraintExpr(Sema &S, const NamedDecl *D,
+ const Expr *E);
+};
- enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
+class CompoundConstraint : public NormalizedConstraint {
+ using NormalizedConstraint::NormalizedConstraint;
+
+public:
+ static CompoundConstraint *Create(ASTContext &Ctx, NormalizedConstraint *LHS,
+ CompoundConstraintKind CCK,
+ NormalizedConstraint *RHS) {
+ return new (Ctx) CompoundConstraint(LHS, CCK, RHS);
+ }
+
+ static CompoundConstraint *CreateConjunction(ASTContext &Ctx,
+ NormalizedConstraint *LHS,
+ NormalizedConstraint *RHS) {
+ return new (Ctx) CompoundConstraint(LHS, CCK_Conjunction, RHS);
+ }
- using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1,
- CompoundConstraintKind>;
+ const NormalizedConstraint &getLHS() const { return *Compound.LHS; }
- llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
- CompoundConstraint>
- Constraint;
+ NormalizedConstraint &getLHS() { return *Compound.LHS; }
- NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
- NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {};
+ const NormalizedConstraint &getRHS() const { return *Compound.RHS; }
- NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
- NormalizedConstraint RHS, CompoundConstraintKind Kind);
+ NormalizedConstraint &getRHS() { return *Compound.RHS; }
- NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other);
- NormalizedConstraint(NormalizedConstraint &&Other):
- Constraint(Other.Constraint) {
- Other.Constraint = nullptr;
+ CompoundConstraintKind getCompoundKind() const {
+ return static_cast<CompoundConstraintKind>(Compound.CCK);
}
- NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete;
- NormalizedConstraint &operator=(NormalizedConstraint &&Other) {
- if (&Other != this) {
- NormalizedConstraint Temp(std::move(Other));
- std::swap(Constraint, Temp.Constraint);
- }
- return *this;
+};
+
+class NormalizedConstraintWithParamMapping : public NormalizedConstraint {
+protected:
+ using NormalizedConstraint::NormalizedConstraint;
+
+public:
+ using NormalizedConstraint::getParameterMapping;
+ using NormalizedConstraint::hasMatchingParameterMapping;
+ using NormalizedConstraint::hasParameterMapping;
+ using NormalizedConstraint::mappingOccurenceList;
+ using NormalizedConstraint::updateParameterMapping;
+
+ const NamedDecl *getConstraintDecl() const { return Atomic.ConstraintDecl; }
+
+ UnsignedOrNone getPackSubstitutionIndex() const {
+ return UnsignedOrNone::fromInternalRepresentation(
+ Atomic.PackSubstitutionIndex);
+ }
+};
+
+class AtomicConstraint : public NormalizedConstraintWithParamMapping {
+ using NormalizedConstraintWithParamMapping::
+ NormalizedConstraintWithParamMapping;
+
+public:
+ static AtomicConstraint *Create(ASTContext &Ctx, const Expr *ConstraintExpr,
+ const NamedDecl *ConstraintDecl,
+ UnsignedOrNone PackIndex) {
+ return new (Ctx)
+ AtomicConstraint(ConstraintExpr, ConstraintDecl, PackIndex);
}
- bool isAtomic() const { return llvm::isa<AtomicConstraint *>(Constraint); }
- bool isFoldExpanded() const {
- return llvm::isa<FoldExpandedConstraint *>(Constraint);
+ const Expr *getConstraintExpr() const {
+ return cast<const Expr *>(Atomic.ConstraintExpr);
}
- bool isCompound() const { return llvm::isa<CompoundConstraint>(Constraint); }
- CompoundConstraintKind getCompoundKind() const;
+ void InitParameterMapping(const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+ NormalizedConstraint::InitParameterMapping(
+ cast<TemplateDecl>(Atomic.ConstraintDecl)->getTemplateParameters(),
+ getConstraintExpr(), ArgsAsWritten);
+ }
+};
- NormalizedConstraint &getLHS() const;
- NormalizedConstraint &getRHS() const;
+class FoldExpandedConstraint : public NormalizedConstraint {
+ using NormalizedConstraint::NormalizedConstraint;
- AtomicConstraint *getAtomicConstraint() const;
+public:
+ static FoldExpandedConstraint *Create(ASTContext &Ctx, const Expr *Pattern,
+ FoldOperatorKind OpKind,
+ NormalizedConstraint *Constraint) {
+ return new (Ctx) FoldExpandedConstraint(Pattern, OpKind, Constraint);
+ }
- FoldExpandedConstraint *getFoldExpandedConstraint() const;
+ using NormalizedConstraint::hasMatchingParameterMapping;
-private:
- static std::optional<NormalizedConstraint>
- fromAssociatedConstraints(Sema &S, const NamedDecl *D,
- ArrayRef<AssociatedConstraint> ACs);
- static std::optional<NormalizedConstraint>
- fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E);
-};
+ FoldOperatorKind getFoldOperator() const {
+ return static_cast<FoldOperatorKind>(FoldExpanded.FoldOperator);
+ }
-struct alignas(ConstraintAlignment) NormalizedConstraintPair {
- NormalizedConstraint LHS, RHS;
-};
+ const Expr *getPattern() const { return FoldExpanded.Pattern; }
-struct alignas(ConstraintAlignment) FoldExpandedConstraint {
- enum class FoldOperatorKind { And, Or } Kind;
- NormalizedConstraint Constraint;
- const Expr *Pattern;
+ const NormalizedConstraint &getNormalizedPattern() const {
+ return *FoldExpanded.Constraint;
+ }
- FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C,
- const Expr *Pattern)
- : Kind(K), Constraint(std::move(C)), Pattern(Pattern) {};
+ NormalizedConstraint &getNormalizedPattern() {
+ return *FoldExpanded.Constraint;
+ }
static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A,
const FoldExpandedConstraint &B);
};
-const NormalizedConstraint *getNormalizedAssociatedConstraints(
- Sema &S, const NamedDecl *ConstrainedDecl,
- ArrayRef<AssociatedConstraint> AssociatedConstraints);
+class ConceptIdConstraint : public NormalizedConstraintWithParamMapping {
+ using NormalizedConstraintWithParamMapping::
+ NormalizedConstraintWithParamMapping;
+
+public:
+ static ConceptIdConstraint *Create(ASTContext &Ctx,
+ const ConceptReference *ConceptId,
+ NormalizedConstraint *SubConstraint,
+ const NamedDecl *ConstraintDecl,
+ UnsignedOrNone PackIndex) {
+ return new (Ctx) ConceptIdConstraint(ConceptId, ConstraintDecl,
+ SubConstraint, PackIndex);
+ }
+
+ const ConceptReference *getConceptId() const {
+ return cast<const ConceptReference *>(ConceptId.ConstraintExpr);
+ }
+
+ const NormalizedConstraint &getNormalizedConstraint() const {
+ return *ConceptId.Sub;
+ }
+
+ NormalizedConstraint &getNormalizedConstraint() { return *ConceptId.Sub; }
+};
/// \brief SubsumptionChecker establishes subsumption
/// between two set of constraints.
@@ -189,13 +414,13 @@ class SubsumptionChecker {
};
struct MappedAtomicConstraint {
- AtomicConstraint *Constraint;
+ const AtomicConstraint *Constraint;
Literal ID;
};
struct FoldExpendedConstraintKey {
FoldExpandedConstraint::FoldOperatorKind Kind;
- AtomicConstraint *Constraint;
+ const AtomicConstraint *Constraint;
Literal ID;
};
@@ -207,7 +432,7 @@ class SubsumptionChecker {
// A map from a literal to a corresponding associated constraint.
// We do not have enough bits left for a pointer union here :(
- llvm::DenseMap<uint16_t, void *> ReverseMap;
+ llvm::DenseMap<uint16_t, const void *> ReverseMap;
// Fold expanded constraints ask us to recursively establish subsumption.
// This caches the result.
@@ -234,8 +459,8 @@ class SubsumptionChecker {
FormulaType Normalize(const NormalizedConstraint &C);
void AddUniqueClauseToFormula(Formula &F, Clause C);
- Literal find(AtomicConstraint *);
- Literal find(FoldExpandedConstraint *);
+ Literal find(const AtomicConstraint *);
+ Literal find(const FoldExpandedConstraint *);
uint16_t getNewLiteralId();
};
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index f9a10cfafb1f7..e6962e5bb1ae5 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -239,10 +239,12 @@ enum class TemplateSubstitutionKind : char {
"Replacing in an empty list?");
if (!TemplateArgumentLists.empty()) {
- assert((TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ||
- TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ==
- AssociatedDecl) &&
- "Trying to change incorrect declaration?");
+ // assert((!TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer()
+ // ||
+ // TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer()
+ // ==
+ // AssociatedDecl) &&
+ // "Trying to change incorrect declaration?");
TemplateArgumentLists[0].Args = Args;
} else {
--NumRetainedOuterLevels;
@@ -251,6 +253,17 @@ enum class TemplateSubstitutionKind : char {
}
}
+ void replaceOutermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) {
+ assert((!TemplateArgumentLists.empty()) && "Replacing in an empty list?");
+ // assert((!TemplateArgumentLists.back().AssociatedDeclAndFinal.getPointer()
+ // ||
+ // TemplateArgumentLists.back().AssociatedDeclAndFinal.getPointer()
+ // ==
+ // AssociatedDecl) &&
+ // "Trying to change incorrect declaration?");
+ TemplateArgumentLists.back().Args = Args;
+ }
+
/// Add an outermost level that we are not substituting. We have no
/// arguments at this level, and do not remove it from the depth of inner
/// template parameters that we instantiate.
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index c9cfec6bd64b5..d4dfc75fd25d5 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -23,7 +23,9 @@ static void
CreateUnsatisfiedConstraintRecord(const ASTContext &C,
const UnsatisfiedConstraintRecord &Detail,
UnsatisfiedConstraintRecord *TrailingObject) {
- if (auto *E = dyn_cast<Expr *>(Detail))
+ if (Detail.isNull())
+ new (TrailingObject) UnsatisfiedConstraintRecord(nullptr);
+ else if (const auto *E = llvm::dyn_cast<const Expr *>(Detail))
new (TrailingObject) UnsatisfiedConstraintRecord(E);
else {
auto &SubstitutionDiagnostic =
@@ -73,9 +75,10 @@ ASTConstraintSatisfaction *ASTConstraintSatisfaction::Rebuild(
return new (Mem) ASTConstraintSatisfaction(C, Satisfaction);
}
-void ConstraintSatisfaction::Profile(
- llvm::FoldingSetNodeID &ID, const ASTContext &C,
- const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> TemplateArgs) {
+void ConstraintSatisfaction::Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &C,
+ const NamedDecl *ConstraintOwner,
+ ArrayRef<TemplateArgument> TemplateArgs) {
ID.AddPointer(ConstraintOwner);
ID.AddInteger(TemplateArgs.size());
for (auto &Arg : TemplateArgs)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b5417fcf20ddd..698ac64e05232 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5495,6 +5495,7 @@ QualType ASTContext::getSubstTemplateTypeParmType(QualType Replacement,
llvm::FoldingSetNodeID ID;
SubstTemplateTypeParmType::Profile(ID, Replacement, AssociatedDecl, Index,
PackIndex, Final);
+
void *InsertPos = nullptr;
SubstTemplateTypeParmType *SubstParm =
SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos);
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index c6a54dc141ded..7c8d80ba4ca34 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -12,10 +12,16 @@
#include "clang/Sema/SemaConcept.h"
#include "TreeTransform.h"
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTLambda.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprConcepts.h"
+#include "clang/AST/TemplateBase.h"
#include "clang/Basic/OperatorPrecedence.h"
+#include "clang/Basic/UnsignedOrNone.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Overload.h"
@@ -24,9 +30,12 @@
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include <cstddef>
#include <optional>
using namespace clang;
@@ -200,6 +209,50 @@ DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
return false;
}
+// Figure out the to-translation-unit depth for this function declaration for
+// the purpose of seeing if they differ by constraints. This isn't the same as
+// getTemplateDepth, because it includes already instantiated parents.
+static unsigned
+CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
+ bool SkipForSpecialization = false) {
+ MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+ ND, ND->getLexicalDeclContext(), /*Final=*/false,
+ /*Innermost=*/std::nullopt,
+ /*RelativeToPrimary=*/true,
+ /*Pattern=*/nullptr,
+ /*ForConstraintInstantiation=*/true, SkipForSpecialization);
+ return MLTAL.getNumLevels();
+}
+
+namespace {
+class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
+ unsigned TemplateDepth = 0;
+
+public:
+ using inherited = TreeTransform<AdjustConstraintDepth>;
+ AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
+ : inherited(SemaRef), TemplateDepth(TemplateDepth) {}
+
+ using inherited::TransformTemplateTypeParmType;
+ QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
+ TemplateTypeParmTypeLoc TL, bool) {
+ const TemplateTypeParmType *T = TL.getTypePtr();
+
+ TemplateTypeParmDecl *NewTTPDecl = nullptr;
+ if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
+ NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
+ TransformDecl(TL.getNameLoc(), OldTTPDecl));
+
+ QualType Result = getSema().Context.getTemplateTypeParmType(
+ T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
+ NewTTPDecl);
+ TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
+ NewTL.setNameLoc(TL.getNameLoc());
+ return Result;
+ }
+};
+} // namespace
+
static ExprResult EvaluateAtomicConstraint(
Sema &S, const Expr *AtomicExpr, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
@@ -209,7 +262,7 @@ static ExprResult EvaluateAtomicConstraint(
Sema::ReuseLambdaContextDecl);
// Atomic constraint - substitute arguments and check satisfaction.
- ExprResult SubstitutedExpression;
+ ExprResult SubstitutedExpression = const_cast<Expr *>(AtomicExpr);
{
TemplateDeductionInfo Info(TemplateNameLoc);
Sema::InstantiatingTemplate Inst(
@@ -220,7 +273,7 @@ static ExprResult EvaluateAtomicConstraint(
if (Inst.isInvalid())
return ExprError();
- llvm::FoldingSetNodeID ID;
+ /*llvm::FoldingSetNodeID ID;
if (Template &&
DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
Satisfaction.IsSatisfied = false;
@@ -229,9 +282,18 @@ static ExprResult EvaluateAtomicConstraint(
}
SatisfactionStackRAII StackRAII(S, Template, ID);
+ */
// We do not want error diagnostics escaping here.
Sema::SFINAETrap Trap(S);
+
+ if (Template && Template->getTemplateDepth() > 0) {
+ SubstitutedExpression =
+ AdjustConstraintDepth(S, Template->getTemplateDepth() - 1)
+ .TransformExpr(const_cast<Expr *>(AtomicExpr))
+ .get();
+ }
+
SubstitutedExpression =
S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
@@ -289,217 +351,82 @@ static ExprResult EvaluateAtomicConstraint(
return SubstitutedExpression;
}
-static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
- Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template,
+static bool calculateConstraintSatisfaction(
+ Sema &S, const NormalizedConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction) {
+ ConstraintSatisfaction &Satisfaction, UnsignedOrNone PackSubstitutionIndex);
- // We should ignore errors in the presence of packs of different size.
- Sema::SFINAETrap Trap(S);
+static std::optional<MultiLevelTemplateArgumentList>
+SubstitutionInTemplateArguments(
+ Sema &S, const NormalizedConstraintWithParamMapping &Constraint,
+ const NamedDecl *Template, MultiLevelTemplateArgumentList MLTAL,
+ llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) {
- Expr *Pattern = FE->getPattern();
-
- SmallVector<UnexpandedParameterPack, 2> Unexpanded;
- S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
- assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
- bool Expand = true;
- bool RetainExpansion = false;
- UnsignedOrNone NumExpansions = FE->getNumExpansions();
- if (S.CheckParameterPacksForExpansion(
- FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
- Expand, RetainExpansion, NumExpansions) ||
- !Expand || RetainExpansion)
- return std::nullopt;
+ Sema::SFINAETrap Trap(S);
- if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) {
- S.Diag(FE->getEllipsisLoc(),
- clang::diag::err_fold_expression_limit_exceeded)
- << *NumExpansions << S.getLangOpts().BracketDepth
- << FE->getSourceRange();
- S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth);
+ Sema::InstantiatingTemplate Inst(
+ S, Constraint.getBeginLoc(),
+ Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
+ // FIXME: improve const-correctness of InstantiatingTemplate
+ const_cast<NamedDecl *>(Template), Constraint.getSourceRange());
+ if (Inst.isInvalid())
return std::nullopt;
- }
- return NumExpansions;
-}
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template,
- SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction);
+ // TODO substitute at the appropriate depth
+ // Template->getTemplateDepth();
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const Expr *LHS, OverloadedOperatorKind Op, const Expr *RHS,
- const NamedDecl *Template, SourceLocation TemplateNameLoc,
- const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction) {
- size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
-
- ExprResult LHSRes = calculateConstraintSatisfaction(
- S, LHS, Template, TemplateNameLoc, MLTAL, Satisfaction);
-
- if (LHSRes.isInvalid())
- return ExprError();
-
- bool IsLHSSatisfied = Satisfaction.IsSatisfied;
-
- if (Op == clang::OO_PipePipe && IsLHSSatisfied)
- // [temp.constr.op] p3
- // A disjunction is a constraint taking two operands. To determine if
- // a disjunction is satisfied, the satisfaction of the first operand
- // is checked. If that is satisfied, the disjunction is satisfied.
- // Otherwise, the disjunction is satisfied if and only if the second
- // operand is satisfied.
- // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
- return LHSRes;
-
- if (Op == clang::OO_AmpAmp && !IsLHSSatisfied)
- // [temp.constr.op] p2
- // A conjunction is a constraint taking two operands. To determine if
- // a conjunction is satisfied, the satisfaction of the first operand
- // is checked. If that is not satisfied, the conjunction is not
- // satisfied. Otherwise, the conjunction is satisfied if and only if
- // the second operand is satisfied.
- // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
- return LHSRes;
-
- ExprResult RHSRes = calculateConstraintSatisfaction(
- S, RHS, Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (RHSRes.isInvalid())
- return ExprError();
-
- bool IsRHSSatisfied = Satisfaction.IsSatisfied;
- // Current implementation adds diagnostic information about the falsity
- // of each false atomic constraint expression when it evaluates them.
- // When the evaluation results to `false || true`, the information
- // generated during the evaluation of left-hand side is meaningless
- // because the whole expression evaluates to true.
- // The following code removes the irrelevant diagnostic information.
- // FIXME: We should probably delay the addition of diagnostic information
- // until we know the entire expression is false.
- if (Op == clang::OO_PipePipe && IsRHSSatisfied) {
- auto EffectiveDetailEnd = Satisfaction.Details.begin();
- std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
- Satisfaction.Details.erase(EffectiveDetailEnd, Satisfaction.Details.end());
- }
-
- if (!LHSRes.isUsable() || !RHSRes.isUsable())
- return ExprEmpty();
-
- return BinaryOperator::Create(S.Context, LHSRes.get(), RHSRes.get(),
- BinaryOperator::getOverloadedOpcode(Op),
- S.Context.BoolTy, VK_PRValue, OK_Ordinary,
- LHS->getBeginLoc(), FPOptionsOverride{});
-}
-
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template,
- SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction) {
- bool Conjunction = FE->getOperator() == BinaryOperatorKind::BO_LAnd;
- size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
-
- ExprResult Out;
- if (FE->isLeftFold() && FE->getInit()) {
- Out = calculateConstraintSatisfaction(S, FE->getInit(), Template,
- TemplateNameLoc, MLTAL, Satisfaction);
- if (Out.isInvalid())
- return ExprError();
-
- // If the first clause of a conjunction is not satisfied,
- // or if the first clause of a disjection is satisfied,
- // we have established satisfaction of the whole constraint
- // and we should not continue further.
- if (Conjunction != Satisfaction.IsSatisfied)
- return Out;
- }
- UnsignedOrNone NumExpansions = EvaluateFoldExpandedConstraintSize(
- S, FE, Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (!NumExpansions)
- return ExprError();
- for (unsigned I = 0; I < *NumExpansions; I++) {
- Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
- ExprResult Res = calculateConstraintSatisfaction(
- S, FE->getPattern(), Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (Res.isInvalid())
- return ExprError();
- bool IsRHSSatisfied = Satisfaction.IsSatisfied;
- if (!Conjunction && IsRHSSatisfied) {
- auto EffectiveDetailEnd = Satisfaction.Details.begin();
- std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
- Satisfaction.Details.erase(EffectiveDetailEnd,
- Satisfaction.Details.end());
- }
- if (Out.isUnset())
- Out = Res;
- else if (!Res.isUnset()) {
- Out = BinaryOperator::Create(
- S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
- VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
- }
- if (Conjunction != IsRHSSatisfied)
- return Out;
- }
-
- if (FE->isRightFold() && FE->getInit()) {
- ExprResult Res = calculateConstraintSatisfaction(
- S, FE->getInit(), Template, TemplateNameLoc, MLTAL, Satisfaction);
- if (Out.isInvalid())
- return ExprError();
-
- if (Out.isUnset())
- Out = Res;
- else if (!Res.isUnset()) {
- Out = BinaryOperator::Create(
- S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
- VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
- }
- }
-
- if (Out.isUnset()) {
- Satisfaction.IsSatisfied = Conjunction;
- Out = S.BuildEmptyCXXFoldExpr(FE->getBeginLoc(), FE->getOperator());
+ TemplateArgumentListInfo SubstArgs;
+ if (Constraint.hasParameterMapping()) {
+ if (S.SubstTemplateArgumentsInParameterMapping(
+ Constraint.getParameterMapping(), MLTAL, SubstArgs) ||
+ Trap.hasErrorOccurred())
+ return std::nullopt;
+ NormalizedConstraint::OccurenceList Used =
+ Constraint.mappingOccurenceList();
+ SubstitutedOuterMost =
+ llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
+ for (unsigned I = 0, MappedIndex = 0; I < SubstArgs.size(); I++)
+ if (I < Used.size() && Used[I]) {
+ // SubstitutedOuterMost[I].dump();
+ // SubstArgs[MappedIndex].getArgument().dump();
+ if (I < SubstitutedOuterMost.size())
+ SubstitutedOuterMost[I] = SubstArgs[MappedIndex++].getArgument();
+ else
+ SubstitutedOuterMost.push_back(
+ SubstArgs[MappedIndex++].getArgument());
+ }
+ MLTAL.replaceOutermostTemplateArguments(
+ const_cast<NamedDecl *>(Constraint.getConstraintDecl()),
+ SubstitutedOuterMost);
}
- return Out;
+ return std::move(MLTAL);
}
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template,
+static bool calculateConstraintSatisfaction(
+ Sema &S, const AtomicConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
- ConstraintSatisfaction &Satisfaction) {
- ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
-
- if (LogicalBinOp BO = ConstraintExpr)
- return calculateConstraintSatisfaction(
- S, BO.getLHS(), BO.getOp(), BO.getRHS(), Template, TemplateNameLoc,
- MLTAL, Satisfaction);
+ ConstraintSatisfaction &Satisfaction,
+ UnsignedOrNone PackSubstitutionIndex) {
+
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
+ SubstitutedOuterMost);
+ if (!SubstitutedArgs)
+ return false;
- if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
- // These aren't evaluated, so we don't care about cleanups, so we can just
- // evaluate these as if the cleanups didn't exist.
- return calculateConstraintSatisfaction(
- S, C->getSubExpr(), Template, TemplateNameLoc, MLTAL, Satisfaction);
- }
+ Sema::ArgPackSubstIndexRAII(S, PackSubstitutionIndex);
+ ExprResult SubstitutedAtomicExpr =
+ EvaluateAtomicConstraint(S, Constraint.getConstraintExpr(), Template,
+ TemplateNameLoc, *SubstitutedArgs, Satisfaction);
- if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr);
- FE && S.getLangOpts().CPlusPlus26 &&
- (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
- FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
- return calculateConstraintSatisfaction(S, FE, Template, TemplateNameLoc,
- MLTAL, Satisfaction);
+ if (SubstitutedAtomicExpr.isInvalid()) {
+ return false;
}
- // FIXME: We should not treat ConceptSpecializationExpr as atomic constraints.
-
- // An atomic constraint expression
- ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint(
- S, ConstraintExpr, Template, TemplateNameLoc, MLTAL, Satisfaction);
-
- if (SubstitutedAtomicExpr.isInvalid())
- return ExprError();
-
if (!SubstitutedAtomicExpr.isUsable())
// Evaluator has decided satisfaction without yielding an expression.
- return ExprEmpty();
+ return true;
// We don't have the ability to evaluate this, since it contains a
// RecoveryExpr, so we want to fail overload resolution. Otherwise,
@@ -521,7 +448,13 @@ static ExprResult calculateConstraintSatisfaction(
new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
SubstitutedAtomicExpr.get()->getBeginLoc(),
StringRef(Mem, MessageSize)});
- return SubstitutedAtomicExpr;
+ return true;
+ }
+
+ if (SubstitutedAtomicExpr.get()->isValueDependent()) {
+ Satisfaction.IsSatisfied = true;
+ Satisfaction.ContainsErrors = false;
+ return true;
}
EnterExpressionEvaluationContext ConstantEvaluated(
@@ -539,7 +472,7 @@ static ExprResult calculateConstraintSatisfaction(
<< SubstitutedAtomicExpr.get()->getSourceRange();
for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
S.Diag(PDiag.first, PDiag.second);
- return ExprError();
+ return false;
}
assert(EvalResult.Val.isInt() &&
@@ -548,24 +481,215 @@ static ExprResult calculateConstraintSatisfaction(
if (!Satisfaction.IsSatisfied)
Satisfaction.Details.emplace_back(SubstitutedAtomicExpr.get());
- return SubstitutedAtomicExpr;
+ return SubstitutedAtomicExpr.isUsable();
}
-static ExprResult calculateConstraintSatisfaction(
- Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
- const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
+static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
+ Sema &S, const FoldExpandedConstraint &FE, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction) {
- return calculateConstraintSatisfaction(S, ConstraintExpr, Template,
- TemplateNameLoc, MLTAL, Satisfaction);
+ // We should ignore errors in the presence of packs of different size.
+ Sema::SFINAETrap Trap(S);
+
+ Expr *Pattern = const_cast<Expr *>(FE.getPattern());
+
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+ assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+ bool Expand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions(std::nullopt);
+ if (S.CheckParameterPacksForExpansion(
+ Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
+ Expand, RetainExpansion, NumExpansions) ||
+ !Expand || RetainExpansion)
+ return std::nullopt;
+
+ if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) {
+ S.Diag(Pattern->getExprLoc(),
+ clang::diag::err_fold_expression_limit_exceeded)
+ << *NumExpansions << S.getLangOpts().BracketDepth
+ << Pattern->getSourceRange();
+ S.Diag(Pattern->getExprLoc(), diag::note_bracket_depth);
+ return std::nullopt;
+ }
+ return NumExpansions;
+}
+
+static bool calculateConstraintSatisfaction(
+ Sema &S, const FoldExpandedConstraint &FE, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+ ConstraintSatisfaction &Satisfaction) {
+
+ bool Conjunction =
+ FE.getFoldOperator() == FoldExpandedConstraint::FoldOperatorKind::And;
+ size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
+
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(
+ S,
+ static_cast<const NormalizedConstraintWithParamMapping &>(
+ FE.getNormalizedPattern()),
+ Template, MLTAL, SubstitutedOuterMost);
+ if (!SubstitutedArgs)
+ return false;
+
+ ExprResult Out;
+ UnsignedOrNone NumExpansions = EvaluateFoldExpandedConstraintSize(
+ S, FE, Template, TemplateNameLoc, *SubstitutedArgs, Satisfaction);
+ if (!NumExpansions)
+ return false;
+
+ for (unsigned I = 0; I < *NumExpansions; I++) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
+ bool Success = calculateConstraintSatisfaction(
+ S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
+ *SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
+ if (!Success)
+ return false;
+ bool IsRHSSatisfied = Satisfaction.IsSatisfied;
+ if (!Conjunction && IsRHSSatisfied) {
+ auto EffectiveDetailEnd = Satisfaction.Details.begin();
+ std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
+ Satisfaction.Details.erase(EffectiveDetailEnd,
+ Satisfaction.Details.end());
+ break;
+ }
+ }
+ return true;
+}
+
+static bool calculateConstraintSatisfaction(
+ Sema &S, const ConceptIdConstraint &Constraint, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+ ConstraintSatisfaction &Satisfaction,
+ UnsignedOrNone PackSubstitutionIndex) {
+
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
+ SubstitutedOuterMost);
+
+ Sema::InstantiatingTemplate Tpl(
+ S, Constraint.getConceptId()->getBeginLoc(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{},
+ Constraint.getConceptId()->getNamedConcept(), MLTAL.getInnermost(),
+ Constraint.getSourceRange());
+
+ auto Size = Satisfaction.Details.size();
+
+ bool Ok = calculateConstraintSatisfaction(
+ S, Constraint.getNormalizedConstraint(), Template, TemplateNameLoc, MLTAL,
+ Satisfaction, PackSubstitutionIndex);
+
+ if (Size != Satisfaction.Details.size()) {
+
+ if (!SubstitutedArgs)
+ return Ok;
+
+ Sema::SFINAETrap Trap(S);
+
+ const ASTTemplateArgumentListInfo *Ori =
+ Constraint.getConceptId()->getTemplateArgsAsWritten();
+ TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc);
+ if (Template && Template->getTemplateDepth() > 0) {
+ TemplateArgumentListInfo TransArgs(Ori->LAngleLoc, Ori->RAngleLoc);
+ AdjustConstraintDepth Adjust(S, Template->getTemplateDepth() - 1);
+ if (Adjust.TransformTemplateArguments(Ori->getTemplateArgs(),
+ Ori->NumTemplateArgs, TransArgs))
+ return Ok;
+ if (S.SubstTemplateArguments(TransArgs.arguments(), MLTAL, OutArgs))
+ return Ok;
+ } else {
+ if (S.SubstTemplateArguments(Ori->arguments(), MLTAL, OutArgs))
+ return Ok;
+ }
+
+ CXXScopeSpec SS;
+ SS.Adopt(Constraint.getConceptId()->getNestedNameSpecifierLoc());
+ ExprResult SubstitutedConceptId = S.CheckConceptTemplateId(
+ SS, Constraint.getConceptId()->getTemplateKWLoc(),
+ Constraint.getConceptId()->getConceptNameInfo(),
+ Constraint.getConceptId()->getFoundDecl(),
+ Constraint.getConceptId()->getNamedConcept(), &OutArgs,
+ /*CheckConstraintSatisfaction=*/false);
+
+ if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
+ return Ok;
+
+ Satisfaction.Details.insert(
+ Satisfaction.Details.begin() + Size,
+ ConstraintSatisfaction::Detail(SubstitutedConceptId.get()));
+
+ Satisfaction.Details.push_back(nullptr);
+ }
+ return Ok;
+}
+
+static bool calculateConstraintSatisfaction(
+ Sema &S, const CompoundConstraint &Constraint, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+ ConstraintSatisfaction &Satisfaction,
+ UnsignedOrNone PackSubstitutionIndex) {
+
+ bool Ok = calculateConstraintSatisfaction(
+ S, Constraint.getLHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
+ PackSubstitutionIndex);
+
+ if (!Ok || Satisfaction.ContainsErrors)
+ return false;
+
+ if (Satisfaction.IsSatisfied &&
+ Constraint.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) {
+ return true;
+ }
+ if (!Satisfaction.IsSatisfied &&
+ Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction)
+ return true;
+
+ return calculateConstraintSatisfaction(S, Constraint.getRHS(), Template,
+ TemplateNameLoc, MLTAL, Satisfaction,
+ PackSubstitutionIndex);
+}
+
+static bool calculateConstraintSatisfaction(
+ Sema &S, const NormalizedConstraint &Constraint, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+ ConstraintSatisfaction &Satisfaction,
+ UnsignedOrNone PackSubstitutionIndex) {
+
+ switch (Constraint.getKind()) {
+ case NormalizedConstraint::ConstraintKind::Atomic:
+ return calculateConstraintSatisfaction(
+ S, static_cast<const AtomicConstraint &>(Constraint), Template,
+ TemplateNameLoc, MLTAL, Satisfaction, PackSubstitutionIndex);
+
+ case NormalizedConstraint::ConstraintKind::FoldExpanded:
+ return calculateConstraintSatisfaction(
+ S, static_cast<const FoldExpandedConstraint &>(Constraint), Template,
+ TemplateNameLoc, MLTAL, Satisfaction);
+
+ case NormalizedConstraint::ConstraintKind::ConceptId:
+ return calculateConstraintSatisfaction(
+ S, static_cast<const ConceptIdConstraint &>(Constraint), Template,
+ TemplateNameLoc, MLTAL, Satisfaction, PackSubstitutionIndex);
+
+ case NormalizedConstraint::ConstraintKind::Compound:
+ return calculateConstraintSatisfaction(
+ S, static_cast<const CompoundConstraint &>(Constraint), Template,
+ TemplateNameLoc, MLTAL, Satisfaction, PackSubstitutionIndex);
+ }
}
static bool CheckConstraintSatisfaction(
Sema &S, const NamedDecl *Template,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
- llvm::SmallVectorImpl<Expr *> &Converted,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
- SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
+ SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
+ const ConceptReference *TopLevelConceptId = nullptr) {
+
if (AssociatedConstraints.empty()) {
Satisfaction.IsSatisfied = true;
return false;
@@ -577,54 +701,51 @@ static bool CheckConstraintSatisfaction(
return false;
}
- ArrayRef<TemplateArgument> TemplateArgs =
- TemplateArgsLists.getNumSubstitutedLevels() > 0
- ? TemplateArgsLists.getOutermost()
- : ArrayRef<TemplateArgument>{};
- Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
- Sema::InstantiatingTemplate::ConstraintsCheck{},
- const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
- if (Inst.isInvalid())
- return true;
+ llvm::ArrayRef<TemplateArgument> Args;
+ if (TemplateArgsLists.getNumLevels() != 0)
+ Args = TemplateArgsLists.getInnermost();
- for (const AssociatedConstraint &AC : AssociatedConstraints) {
- Sema::ArgPackSubstIndexRAII _(S, AC.ArgPackSubstIndex);
- ExprResult Res = calculateConstraintSatisfaction(
- S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
- AC.ConstraintExpr, Satisfaction);
- if (Res.isInvalid())
- return true;
+ std::optional<Sema::InstantiatingTemplate> SynthesisContext;
+ if (!TopLevelConceptId) {
+ SynthesisContext.emplace(S, TemplateIDRange.getBegin(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{},
+ const_cast<NamedDecl *>(Template), Args,
+ TemplateIDRange);
+ }
- Converted.push_back(Res.get());
- if (!Satisfaction.IsSatisfied) {
- // Backfill the 'converted' list with nulls so we can keep the Converted
- // and unconverted lists in sync.
- 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
- // conjunction is not satisfied. [...]
- return false;
- }
+ const NormalizedConstraint *C =
+ S.getNormalizedAssociatedConstraints(Template, AssociatedConstraints);
+ if (!C) {
+ Satisfaction.IsSatisfied = false;
+ return true;
}
- return false;
+
+ if (TopLevelConceptId) {
+ C = ConceptIdConstraint::Create(S.getASTContext(), TopLevelConceptId,
+ const_cast<NormalizedConstraint *>(C),
+ Template, S.ArgPackSubstIndex);
+ }
+
+ return !calculateConstraintSatisfaction(
+ S, *C, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
+ Satisfaction, S.ArgPackSubstIndex);
}
bool Sema::CheckConstraintSatisfaction(
- const NamedDecl *Template,
+ ConstrainedDeclOrNestedRequirement Entity,
ArrayRef<AssociatedConstraint> AssociatedConstraints,
- llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
- SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
+ SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction,
+ const ConceptReference *TopLevelConceptId) {
if (AssociatedConstraints.empty()) {
OutSatisfaction.IsSatisfied = true;
return false;
}
+ const auto *Template = Entity.dyn_cast<const NamedDecl *>();
if (!Template) {
- return ::CheckConstraintSatisfaction(
- *this, nullptr, AssociatedConstraints, ConvertedConstraints,
- TemplateArgsLists, TemplateIDRange, OutSatisfaction);
+ return ::CheckConstraintSatisfaction(*this, nullptr, AssociatedConstraints,
+ TemplateArgsLists, TemplateIDRange,
+ OutSatisfaction, TopLevelConceptId);
}
// Invalid templates could make their way here. Substituting them could result
// in dependent expressions.
@@ -641,8 +762,12 @@ bool Sema::CheckConstraintSatisfaction(
for (auto List : TemplateArgsLists)
llvm::append_range(FlattenedArgs, List.Args);
+ const NamedDecl *Owner = Template;
+ if (TopLevelConceptId)
+ Owner = TopLevelConceptId->getNamedConcept();
+
llvm::FoldingSetNodeID ID;
- ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
+ ConstraintSatisfaction::Profile(ID, Context, Owner, FlattenedArgs);
void *InsertPos;
if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
OutSatisfaction = *Cached;
@@ -650,10 +775,10 @@ bool Sema::CheckConstraintSatisfaction(
}
auto Satisfaction =
- std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
+ std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs);
if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints,
- ConvertedConstraints, TemplateArgsLists,
- TemplateIDRange, *Satisfaction)) {
+ TemplateArgsLists, TemplateIDRange,
+ *Satisfaction, TopLevelConceptId)) {
OutSatisfaction = *Satisfaction;
return true;
}
@@ -684,14 +809,18 @@ bool Sema::CheckConstraintSatisfaction(
const ConceptSpecializationExpr *ConstraintExpr,
ConstraintSatisfaction &Satisfaction) {
+ llvm::SmallVector<AssociatedConstraint, 1> Constraints;
+ Constraints.emplace_back(
+ ConstraintExpr->getNamedConcept()->getConstraintExpr());
+
MultiLevelTemplateArgumentList MLTAL(ConstraintExpr->getNamedConcept(),
ConstraintExpr->getTemplateArguments(),
true);
- return calculateConstraintSatisfaction(
- *this, ConstraintExpr, ConstraintExpr->getNamedConcept(),
- ConstraintExpr->getConceptNameLoc(), MLTAL, Satisfaction)
- .isInvalid();
+ return CheckConstraintSatisfaction(
+ ConstraintExpr->getNamedConcept(), Constraints, MLTAL,
+ ConstraintExpr->getSourceRange(), Satisfaction,
+ ConstraintExpr->getConceptReference());
}
bool Sema::SetupConstraintScope(
@@ -850,50 +979,6 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
Satisfaction);
}
-
-// Figure out the to-translation-unit depth for this function declaration for
-// the purpose of seeing if they differ by constraints. This isn't the same as
-// getTemplateDepth, because it includes already instantiated parents.
-static unsigned
-CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
- bool SkipForSpecialization = false) {
- MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
- ND, ND->getLexicalDeclContext(), /*Final=*/false,
- /*Innermost=*/std::nullopt,
- /*RelativeToPrimary=*/true,
- /*Pattern=*/nullptr,
- /*ForConstraintInstantiation=*/true, SkipForSpecialization);
- return MLTAL.getNumLevels();
-}
-
-namespace {
- class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
- unsigned TemplateDepth = 0;
- public:
- using inherited = TreeTransform<AdjustConstraintDepth>;
- AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
- : inherited(SemaRef), TemplateDepth(TemplateDepth) {}
-
- using inherited::TransformTemplateTypeParmType;
- QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
- TemplateTypeParmTypeLoc TL, bool) {
- const TemplateTypeParmType *T = TL.getTypePtr();
-
- TemplateTypeParmDecl *NewTTPDecl = nullptr;
- if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
- NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
- TransformDecl(TL.getNameLoc(), OldTTPDecl));
-
- QualType Result = getSema().Context.getTemplateTypeParmType(
- T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
- NewTTPDecl);
- TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
- NewTL.setNameLoc(TL.getNameLoc());
- return Result;
- }
- };
-} // namespace
-
static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo,
const Expr *ConstrExpr) {
@@ -1146,20 +1231,7 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: {
ConceptSpecializationExpr *ConstraintExpr =
Req->getReturnTypeRequirementSubstitutedConstraintExpr();
- if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
- // A simple case - expr type is the type being constrained and the concept
- // was not provided arguments.
- Expr *e = Req->getExpr();
- S.Diag(e->getBeginLoc(),
- diag::note_expr_requirement_constraints_not_satisfied_simple)
- << (int)First << S.Context.getReferenceQualifiedType(e)
- << ConstraintExpr->getNamedConcept();
- } else {
- S.Diag(ConstraintExpr->getBeginLoc(),
- diag::note_expr_requirement_constraints_not_satisfied)
- << (int)First << ConstraintExpr;
- }
- S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction());
+ S.DiagnoseUnsatisfiedConstraint(ConstraintExpr);
break;
}
case concepts::ExprRequirement::SS_Satisfied:
@@ -1193,31 +1265,63 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
return;
}
}
+
+static void
+diagnoseUnsatisfiedConceptIdExpr(Sema &S, const ConceptSpecializationExpr *CSE,
+ SourceLocation Loc, bool First) {
+ if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
+ S.Diag(
+ Loc,
+ diag::
+ note_single_arg_concept_specialization_constraint_evaluated_to_false)
+ << (int)First
+ << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
+ << CSE->getNamedConcept();
+ } else {
+ S.Diag(Loc, diag::note_concept_specialization_constraint_evaluated_to_false)
+ << (int)First << CSE;
+ }
+}
+
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
- Expr *SubstExpr,
+ const Expr *SubstExpr,
bool First = true);
static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::NestedRequirement *Req,
bool First) {
using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
+ std::vector<bool> Prevs;
for (auto &Record : Req->getConstraintSatisfaction()) {
if (auto *SubstDiag = Record.dyn_cast<SubstitutionDiagnostic *>())
S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error)
<< (int)First << Req->getInvalidConstraintEntity()
<< SubstDiag->second;
- else
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, Record.dyn_cast<Expr *>(),
- First);
+ else {
+ if (Record.isNull()) {
+ First = Prevs.back();
+ Prevs.pop_back();
+ continue;
+ }
+ diagnoseWellFormedUnsatisfiedConstraintExpr(
+ S, Record.dyn_cast<const Expr *>(), First);
+ if (isa_and_nonnull<ConceptSpecializationExpr>(
+ Record.dyn_cast<const Expr *>())) {
+ Prevs.push_back(First);
+ First = true;
+ continue;
+ }
+ First = false;
+ }
First = false;
}
}
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
- Expr *SubstExpr,
+ const Expr *SubstExpr,
bool First) {
SubstExpr = SubstExpr->IgnoreParenImpCasts();
- if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
+ if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
switch (BO->getOpcode()) {
// These two cases will in practice only be reached when using fold
// expressions with || and &&, since otherwise the || and && will have been
@@ -1278,22 +1382,6 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
default:
break;
}
- } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
- if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
- S.Diag(
- CSE->getSourceRange().getBegin(),
- diag::
- note_single_arg_concept_specialization_constraint_evaluated_to_false)
- << (int)First
- << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
- << CSE->getNamedConcept();
- } else {
- S.Diag(SubstExpr->getSourceRange().getBegin(),
- diag::note_concept_specialization_constraint_evaluated_to_false)
- << (int)First << CSE;
- }
- S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
- return;
} else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
// FIXME: RequiresExpr should store dependent diagnostics.
for (concepts::Requirement *Req : RE->getRequirements())
@@ -1323,101 +1411,132 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
S.DiagnoseTypeTraitDetails(SubstExpr);
}
-template <typename SubstitutionDiagnostic>
-static void diagnoseUnsatisfiedConstraintExpr(
- Sema &S, const llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> &Record,
- bool First = true) {
- if (auto *Diag = Record.template dyn_cast<SubstitutionDiagnostic *>()) {
+static void
+diagnoseUnsatisfiedConstraintExpr(Sema &S,
+ const ConstraintSatisfaction::Detail &Record,
+ SourceLocation Loc, bool First = true) {
+ if (auto *Diag = Record.template dyn_cast<
+ ConstraintSatisfaction::SubstitutionDiagnostic *>()) {
S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
<< Diag->second;
return;
}
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, cast<Expr *>(Record), First);
+ const auto *Expr = cast<const class Expr *>(Record);
+ if (const auto *CSE = dyn_cast<ConceptSpecializationExpr>(Expr)) {
+ if (Loc.isInvalid())
+ Loc = CSE->getBeginLoc();
+ diagnoseUnsatisfiedConceptIdExpr(S, CSE, Loc, First);
+ return;
+ }
+ diagnoseWellFormedUnsatisfiedConstraintExpr(S, Expr, First);
}
-void
-Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction,
- bool First) {
+void Sema::DiagnoseUnsatisfiedConstraint(
+ const ConstraintSatisfaction &Satisfaction, SourceLocation Loc,
+ bool First) {
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
+ std::vector<bool> Prevs;
for (auto &Record : Satisfaction.Details) {
- diagnoseUnsatisfiedConstraintExpr(*this, Record, First);
+ if (Record.isNull()) {
+ First = Prevs.back();
+ Prevs.pop_back();
+ continue;
+ }
+ diagnoseUnsatisfiedConstraintExpr(*this, Record, Loc, First);
First = false;
+ Loc = {};
+ if (isa_and_nonnull<ConceptSpecializationExpr>(
+ Record.dyn_cast<const Expr *>())) {
+ Prevs.push_back(First);
+ First = true;
+ }
}
}
void Sema::DiagnoseUnsatisfiedConstraint(
- const ASTConstraintSatisfaction &Satisfaction,
- bool First) {
+ const ConceptSpecializationExpr *ConstraintExpr) {
+ const ASTConstraintSatisfaction &Satisfaction =
+ ConstraintExpr->getSatisfaction();
+ bool First = true;
+ SourceLocation Loc = ConstraintExpr->getBeginLoc();
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
+ std::vector<bool> Prevs;
for (auto &Record : Satisfaction) {
- diagnoseUnsatisfiedConstraintExpr(*this, Record, First);
- First = false;
+ if (Record.isNull()) {
+ First = Prevs.back();
+ Prevs.pop_back();
+ continue;
+ }
+ diagnoseUnsatisfiedConstraintExpr(*this, Record, Loc, First);
+ Loc = {};
+ if (isa_and_nonnull<ConceptSpecializationExpr>(
+ Record.dyn_cast<const Expr *>())) {
+ Prevs.push_back(First);
+ First = true;
+ continue;
+ }
}
}
const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
- const NamedDecl *ConstrainedDecl,
+ ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq,
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.
- ConstrainedDecl = cast<NamedDecl>(ConstrainedDecl->getCanonicalDecl());
- auto CacheEntry = NormalizationCache.find(ConstrainedDecl);
+ if (!ConstrainedDeclOrNestedReq)
+ return NormalizedConstraint::fromAssociatedConstraints(
+ *this, nullptr, AssociatedConstraints);
+
+ const NamedDecl *ND =
+ ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>();
+ if (ND)
+ ND = cast<NamedDecl>(ND->getCanonicalDecl());
+
+ auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq);
if (CacheEntry == NormalizationCache.end()) {
auto Normalized = NormalizedConstraint::fromAssociatedConstraints(
- *this, ConstrainedDecl, AssociatedConstraints);
+ *this, ND, AssociatedConstraints);
CacheEntry =
- NormalizationCache
- .try_emplace(ConstrainedDecl,
- Normalized
- ? new (Context) NormalizedConstraint(
- std::move(*Normalized))
- : nullptr)
+ NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
.first;
}
return CacheEntry->second;
}
-const NormalizedConstraint *clang::getNormalizedAssociatedConstraints(
- Sema &S, const NamedDecl *ConstrainedDecl,
- ArrayRef<AssociatedConstraint> AssociatedConstraints) {
- return S.getNormalizedAssociatedConstraints(ConstrainedDecl,
- AssociatedConstraints);
-}
-
static bool
substituteParameterMappings(Sema &S, NormalizedConstraint &N,
- ConceptDecl *Concept,
const MultiLevelTemplateArgumentList &MLTAL,
- const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+ const ASTTemplateArgumentListInfo *ArgsAsWritten);
- if (N.isCompound()) {
- if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
- ArgsAsWritten))
- return true;
- return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
- ArgsAsWritten);
- }
-
- if (N.isFoldExpanded()) {
- Sema::ArgPackSubstIndexRAII _(S, std::nullopt);
- return substituteParameterMappings(
- S, N.getFoldExpandedConstraint()->Constraint, Concept, MLTAL,
- ArgsAsWritten);
- }
-
- TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
+static bool
+substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten) {
- AtomicConstraint &Atomic = *N.getAtomicConstraint();
+ TemplateParameterList *TemplateParams =
+ cast<TemplateDecl>(N.getConstraintDecl())->getTemplateParameters();
TemplateArgumentListInfo SubstArgs;
- if (!Atomic.ParameterMapping) {
+ if (!N.hasParameterMapping()) {
llvm::SmallBitVector OccurringIndices(TemplateParams->size());
- S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
- /*Depth=*/0, OccurringIndices);
+
+ if (N.getKind() == NormalizedConstraint::ConstraintKind::Atomic) {
+ S.MarkUsedTemplateParameters(
+ static_cast<AtomicConstraint &>(N).getConstraintExpr(),
+ /*OnlyDeduced=*/false,
+ /*Depth=*/0, OccurringIndices);
+ } else if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) {
+ auto Args = static_cast<ConceptIdConstraint &>(N)
+ .getConceptId()
+ ->getTemplateArgsAsWritten();
+ if (Args)
+ S.MarkUsedTemplateParameters(Args->arguments(),
+ /*Depth=*/0, OccurringIndices);
+ }
TemplateArgumentLoc *TempArgs =
new (S.Context) TemplateArgumentLoc[OccurringIndices.count()];
for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
@@ -1436,7 +1555,9 @@ substituteParameterMappings(Sema &S, NormalizedConstraint &N,
ArgsAsWritten->NumTemplateArgs > I
? ArgsAsWritten->arguments()[I].getLocation()
: SourceLocation()));
- Atomic.ParameterMapping.emplace(TempArgs, OccurringIndices.count());
+ N.updateParameterMapping(OccurringIndices,
+ MutableArrayRef<TemplateArgumentLoc>{
+ TempArgs, OccurringIndices.count()});
}
SourceLocation InstLocBegin =
ArgsAsWritten->arguments().empty()
@@ -1449,21 +1570,69 @@ substituteParameterMappings(Sema &S, NormalizedConstraint &N,
Sema::InstantiatingTemplate Inst(
S, InstLocBegin,
Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
- const_cast<NamedDecl *>(Atomic.ConstraintDecl),
+ const_cast<NamedDecl *>(N.getConstraintDecl()),
{InstLocBegin, InstLocEnd});
if (Inst.isInvalid())
return true;
- if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
+ if (S.SubstTemplateArgumentsInParameterMapping(N.getParameterMapping(), MLTAL,
+ SubstArgs))
return true;
TemplateArgumentLoc *TempArgs =
new (S.Context) TemplateArgumentLoc[SubstArgs.size()];
std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(),
TempArgs);
- Atomic.ParameterMapping.emplace(TempArgs, SubstArgs.size());
+ N.updateParameterMapping(
+ N.mappingOccurenceList(),
+ MutableArrayRef<TemplateArgumentLoc>(TempArgs, SubstArgs.size()));
return false;
}
+static bool
+substituteParameterMappings(Sema &S, ConceptIdConstraint &N,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+
+ {
+ if (N.getConstraintDecl()) {
+ substituteParameterMappings(
+ S, static_cast<NormalizedConstraintWithParamMapping &>(N), MLTAL,
+ ArgsAsWritten);
+ }
+ }
+ return substituteParameterMappings(S, N.getNormalizedConstraint(), MLTAL,
+ ArgsAsWritten);
+}
+
+static bool
+substituteParameterMappings(Sema &S, NormalizedConstraint &N,
+ const MultiLevelTemplateArgumentList &MLTAL,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+
+ switch (N.getKind()) {
+ case NormalizedConstraint::ConstraintKind::Atomic:
+ return substituteParameterMappings(S, static_cast<AtomicConstraint &>(N),
+ MLTAL, ArgsAsWritten);
+ case NormalizedConstraint::ConstraintKind::FoldExpanded: {
+ Sema::ArgPackSubstIndexRAII _(S, std::nullopt);
+ return substituteParameterMappings(
+ S, static_cast<FoldExpandedConstraint &>(N).getNormalizedPattern(),
+ MLTAL, ArgsAsWritten);
+ }
+ case NormalizedConstraint::ConstraintKind::ConceptId:
+ return substituteParameterMappings(S, static_cast<ConceptIdConstraint &>(N),
+ MLTAL, ArgsAsWritten);
+
+ case NormalizedConstraint::ConstraintKind::Compound: {
+ auto &Compound = static_cast<CompoundConstraint &>(N);
+ if (substituteParameterMappings(S, Compound.getLHS(), MLTAL, ArgsAsWritten))
+ return true;
+ return substituteParameterMappings(S, Compound.getRHS(), MLTAL,
+ ArgsAsWritten);
+ }
+ }
+}
+
static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
const ConceptSpecializationExpr *CSE) {
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
@@ -1473,64 +1642,27 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
- return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
+ return substituteParameterMappings(S, N, MLTAL,
CSE->getTemplateArgsAsWritten());
}
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
- NormalizedConstraint LHS,
- NormalizedConstraint RHS,
- CompoundConstraintKind Kind)
- : Constraint{CompoundConstraint{
- new(C) NormalizedConstraintPair{std::move(LHS), std::move(RHS)},
- Kind}} {}
-
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
- const NormalizedConstraint &Other) {
- if (Other.isAtomic()) {
- Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
- } else if (Other.isFoldExpanded()) {
- Constraint = new (C) FoldExpandedConstraint(
- Other.getFoldExpandedConstraint()->Kind,
- NormalizedConstraint(C, Other.getFoldExpandedConstraint()->Constraint),
- Other.getFoldExpandedConstraint()->Pattern);
- } else {
- Constraint = CompoundConstraint(
- new (C)
- NormalizedConstraintPair{NormalizedConstraint(C, Other.getLHS()),
- NormalizedConstraint(C, Other.getRHS())},
- Other.getCompoundKind());
- }
-}
-
-NormalizedConstraint &NormalizedConstraint::getLHS() const {
- assert(isCompound() && "getLHS called on a non-compound constraint.");
- return cast<CompoundConstraint>(Constraint).getPointer()->LHS;
-}
-
-NormalizedConstraint &NormalizedConstraint::getRHS() const {
- assert(isCompound() && "getRHS called on a non-compound constraint.");
- return cast<CompoundConstraint>(Constraint).getPointer()->RHS;
-}
-
-std::optional<NormalizedConstraint>
-NormalizedConstraint::fromAssociatedConstraints(
+NormalizedConstraint *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;
+ return nullptr;
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),
- std::move(*Next), CCK_Conjunction);
+ return nullptr;
+ Conjunction = CompoundConstraint::CreateConjunction(S.getASTContext(),
+ Conjunction, Next);
}
return Conjunction;
}
-std::optional<NormalizedConstraint>
+NormalizedConstraint *
NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
const Expr *E) {
assert(E != nullptr);
@@ -1549,15 +1681,15 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
if (LogicalBinOp BO = E) {
auto LHS = fromConstraintExpr(S, D, BO.getLHS());
if (!LHS)
- return std::nullopt;
+ return nullptr;
auto RHS = fromConstraintExpr(S, D, BO.getRHS());
if (!RHS)
- return std::nullopt;
+ return nullptr;
- return NormalizedConstraint(S.Context, std::move(*LHS), std::move(*RHS),
- BO.isAnd() ? CCK_Conjunction : CCK_Disjunction);
+ return CompoundConstraint::Create(
+ S.Context, LHS, BO.isAnd() ? CCK_Conjunction : CCK_Disjunction, RHS);
} else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) {
- const NormalizedConstraint *SubNF;
+ NormalizedConstraint *SubNF;
{
Sema::InstantiatingTemplate Inst(
S, CSE->getExprLoc(),
@@ -1565,7 +1697,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// FIXME: improve const-correctness of InstantiatingTemplate
const_cast<NamedDecl *>(D), CSE->getSourceRange());
if (Inst.isInvalid())
- return std::nullopt;
+ return nullptr;
// C++ [temp.constr.normal]p1.1
// [...]
// The normal form of an id-expression of the form C<A1, A2, ..., AN>,
@@ -1576,23 +1708,23 @@ 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, AssociatedConstraint(CD->getConstraintExpr()));
+ SubNF = NormalizedConstraint::fromAssociatedConstraints(
+ S, CSE->getNamedConcept(),
+ AssociatedConstraint(CD->getConstraintExpr()));
+
if (!SubNF)
- return std::nullopt;
+ return nullptr;
}
+ if (substituteParameterMappings(S, *SubNF, CSE))
+ return nullptr;
- std::optional<NormalizedConstraint> New;
- New.emplace(S.Context, *SubNF);
+ return ConceptIdConstraint::Create(S.getASTContext(),
+ CSE->getConceptReference(), SubNF, D,
+ S.ArgPackSubstIndex);
- if (substituteParameterMappings(S, *New, CSE))
- return std::nullopt;
-
- return New;
} else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
- FE && S.getLangOpts().CPlusPlus26 &&
- (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
- FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
+ FE && (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
+ FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
// Normalize fold expressions in C++26.
@@ -1605,28 +1737,28 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
auto LHS = fromConstraintExpr(S, D, FE->getLHS());
auto RHS = fromConstraintExpr(S, D, FE->getRHS());
if (!LHS || !RHS)
- return std::nullopt;
+ return nullptr;
if (FE->isRightFold())
- RHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*RHS), FE->getPattern()}};
+ RHS = FoldExpandedConstraint::Create(S.getASTContext(),
+ FE->getPattern(), Kind, RHS);
else
- LHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*LHS), FE->getPattern()}};
-
- return NormalizedConstraint(
- S.Context, std::move(*LHS), std::move(*RHS),
- FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
- : CCK_Disjunction);
+ LHS = FoldExpandedConstraint::Create(S.getASTContext(),
+ FE->getPattern(), Kind, LHS);
+
+ return CompoundConstraint::Create(
+ S.getASTContext(), LHS,
+ (FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
+ : CCK_Disjunction),
+ RHS);
}
auto Sub = fromConstraintExpr(S, D, FE->getPattern());
if (!Sub)
- return std::nullopt;
- return NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
- Kind, std::move(*Sub), FE->getPattern()}};
+ return nullptr;
+ return FoldExpandedConstraint::Create(S.getASTContext(), FE->getPattern(),
+ Kind, Sub);
}
-
- return NormalizedConstraint{new (S.Context) AtomicConstraint(E, D)};
+ return AtomicConstraint::Create(S.getASTContext(), E, D, S.ArgPackSubstIndex);
}
bool FoldExpandedConstraint::AreCompatibleForSubsumption(
@@ -1637,8 +1769,10 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption(
// if their respective constraints both contain an equivalent unexpanded pack.
llvm::SmallVector<UnexpandedParameterPack> APacks, BPacks;
- Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.Pattern), APacks);
- Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks);
+ Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.getPattern()),
+ APacks);
+ Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.getPattern()),
+ BPacks);
for (const UnexpandedParameterPack &APack : APacks) {
std::pair<unsigned, unsigned> DepthAndIndex = getDepthAndIndex(APack);
@@ -1730,7 +1864,7 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
const AtomicConstraint &B) {
if (!A.hasMatchingParameterMapping(Context, B))
return false;
- const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr;
+ const Expr *EA = A.getConstraintExpr(), *EB = B.getConstraintExpr();
if (EA == EB)
return true;
@@ -1783,24 +1917,6 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
return true;
}
-NormalizedConstraint::CompoundConstraintKind
-NormalizedConstraint::getCompoundKind() const {
- assert(isCompound() && "getCompoundKind on a non-compound constraint..");
- return cast<CompoundConstraint>(Constraint).getInt();
-}
-
-AtomicConstraint *NormalizedConstraint::getAtomicConstraint() const {
- assert(isAtomic() && "getAtomicConstraint called on non-atomic constraint.");
- return cast<AtomicConstraint *>(Constraint);
-}
-
-FoldExpandedConstraint *
-NormalizedConstraint::getFoldExpandedConstraint() const {
- assert(isFoldExpanded() &&
- "getFoldExpandedConstraint called on non-fold-expanded constraint.");
- return cast<FoldExpandedConstraint *>(Constraint);
-}
-
//
//
// ------------------------ Subsumption -----------------------------------
@@ -1843,8 +1959,8 @@ uint16_t SubsumptionChecker::getNewLiteralId() {
return NextID++;
}
-auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
- auto &Elems = AtomicMap[Ori->ConstraintExpr];
+auto SubsumptionChecker::find(const AtomicConstraint *Ori) -> Literal {
+ auto &Elems = AtomicMap[Ori->getConstraintExpr()];
// C++ [temp.constr.order] p2
// - an atomic constraint A subsumes another atomic constraint B
// if and only if the A and B are identical [...]
@@ -1860,10 +1976,10 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
// subsumes another, their literal will be the same
llvm::FoldingSetNodeID ID;
- const auto &Mapping = Ori->ParameterMapping;
- ID.AddBoolean(Mapping.has_value());
- if (Mapping) {
- for (const TemplateArgumentLoc &TAL : *Mapping) {
+ ID.AddBoolean(Ori->hasParameterMapping());
+ if (Ori->hasParameterMapping()) {
+ const auto &Mapping = Ori->getParameterMapping();
+ for (const TemplateArgumentLoc &TAL : Mapping) {
SemaRef.getASTContext()
.getCanonicalTemplateArgument(TAL.getArgument())
.Profile(ID, SemaRef.getASTContext());
@@ -1881,11 +1997,11 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
return It->getSecond().ID;
}
-auto SubsumptionChecker::find(FoldExpandedConstraint *Ori) -> Literal {
- auto &Elems = FoldMap[Ori->Pattern];
+auto SubsumptionChecker::find(const FoldExpandedConstraint *Ori) -> Literal {
+ auto &Elems = FoldMap[Ori->getPattern()];
FoldExpendedConstraintKey K;
- K.Kind = Ori->Kind;
+ K.Kind = Ori->getFoldOperator();
auto It = llvm::find_if(Elems, [&K](const FoldExpendedConstraintKey &Other) {
return K.Kind == Other.Kind;
@@ -1929,38 +2045,47 @@ FormulaType SubsumptionChecker::Normalize(const NormalizedConstraint &NC) {
AddUniqueClauseToFormula(Res, std::move(C));
};
- if (NC.isAtomic())
- return {{find(NC.getAtomicConstraint())}};
+ switch (NC.getKind()) {
- if (NC.isFoldExpanded())
- return {{find(NC.getFoldExpandedConstraint())}};
+ case NormalizedConstraint::ConstraintKind::Atomic:
+ return {{find(&static_cast<const AtomicConstraint &>(NC))}};
- FormulaType Left, Right;
- SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] {
- Left = Normalize<FormulaType>(NC.getLHS());
- Right = Normalize<FormulaType>(NC.getRHS());
- });
+ case NormalizedConstraint::ConstraintKind::FoldExpanded:
+ return {{find(&static_cast<const FoldExpandedConstraint &>(NC))}};
- if (NC.getCompoundKind() == FormulaType::Kind) {
- auto SizeLeft = Left.size();
- Res = std::move(Left);
- Res.reserve(SizeLeft + Right.size());
- std::for_each(std::make_move_iterator(Right.begin()),
- std::make_move_iterator(Right.end()), Add);
- return Res;
- }
+ case NormalizedConstraint::ConstraintKind::ConceptId:
+ return Normalize<FormulaType>(
+ static_cast<const ConceptIdConstraint &>(NC).getNormalizedConstraint());
+
+ case NormalizedConstraint::ConstraintKind::Compound: {
+ const auto &Compound = static_cast<const CompoundConstraint &>(NC);
+ FormulaType Left, Right;
+ SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] {
+ Left = Normalize<FormulaType>(Compound.getLHS());
+ Right = Normalize<FormulaType>(Compound.getRHS());
+ });
+
+ if (Compound.getCompoundKind() == FormulaType::Kind) {
+ Res = std::move(Left);
+ Res.reserve(Left.size() + Right.size());
+ std::for_each(std::make_move_iterator(Right.begin()),
+ std::make_move_iterator(Right.end()), Add);
+ return Res;
+ }
- Res.reserve(Left.size() * Right.size());
- for (const auto <ransform : Left) {
- for (const auto &RTransform : Right) {
- Clause Combined;
- Combined.reserve(LTransform.size() + RTransform.size());
- llvm::append_range(Combined, LTransform);
- llvm::append_range(Combined, RTransform);
- Add(std::move(Combined));
+ Res.reserve(Left.size() * Right.size());
+ for (const auto <ransform : Left) {
+ for (const auto &RTransform : Right) {
+ Clause Combined;
+ Combined.reserve(LTransform.size() + RTransform.size());
+ llvm::copy(LTransform, std::back_inserter(Combined));
+ llvm::copy(RTransform, std::back_inserter(Combined));
+ Add(std::move(Combined));
+ }
}
+ return Res;
+ }
}
- return Res;
}
void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) {
@@ -1975,12 +2100,12 @@ std::optional<bool> SubsumptionChecker::Subsumes(
const NamedDecl *DP, ArrayRef<AssociatedConstraint> P, const NamedDecl *DQ,
ArrayRef<AssociatedConstraint> Q) {
const NormalizedConstraint *PNormalized =
- getNormalizedAssociatedConstraints(SemaRef, DP, P);
+ SemaRef.getNormalizedAssociatedConstraints(DP, P);
if (!PNormalized)
return std::nullopt;
const NormalizedConstraint *QNormalized =
- getNormalizedAssociatedConstraints(SemaRef, DQ, Q);
+ SemaRef.getNormalizedAssociatedConstraints(DQ, Q);
if (!QNormalized)
return std::nullopt;
@@ -2030,9 +2155,9 @@ bool SubsumptionChecker::Subsumes(const FoldExpandedConstraint *A,
// constraint B if they are compatible for subsumption, have the same
// fold-operator, and the constraint of A subsumes that of B.
bool DoesSubsume =
- A->Kind == B->Kind &&
+ A->getFoldOperator() == B->getFoldOperator() &&
FoldExpandedConstraint::AreCompatibleForSubsumption(*A, *B) &&
- Subsumes(&A->Constraint, &B->Constraint);
+ Subsumes(&A->getNormalizedPattern(), &B->getNormalizedPattern());
It = FoldSubsumptionCache.try_emplace(std::move(Key), DoesSubsume).first;
}
return It->second;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 770ac9839eb98..8a0ca8fcc14cf 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17913,13 +17913,15 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
findFailedBooleanCondition(Converted.get());
if (const auto *ConceptIDExpr =
dyn_cast_or_null<ConceptSpecializationExpr>(InnerCond)) {
- // Drill down into concept specialization expressions to see why they
- // weren't satisfied.
- Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
- << !HasMessage << Msg.str() << AssertExpr->getSourceRange();
- ConstraintSatisfaction Satisfaction;
- if (!CheckConstraintSatisfaction(ConceptIDExpr, Satisfaction))
- DiagnoseUnsatisfiedConstraint(Satisfaction);
+ const ASTConstraintSatisfaction &Satisfaction =
+ ConceptIDExpr->getSatisfaction();
+ if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) {
+ Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
+ << !HasMessage << Msg.str() << AssertExpr->getSourceRange();
+ // Drill down into concept specialization expressions to see why they
+ // weren't satisfied.
+ DiagnoseUnsatisfiedConstraint(ConceptIDExpr);
+ }
} else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) &&
!isa<IntegerLiteral>(InnerCond)) {
Diag(InnerCond->getBeginLoc(),
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d8338e08dd0f7..3dbbbe1325675 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8326,7 +8326,14 @@ Sema::BuildExprRequirement(
auto *IDC = TC->getImmediatelyDeclaredConstraint();
assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
ExprResult Constraint = SubstExpr(IDC, MLTAL);
- if (Constraint.isInvalid()) {
+ bool HasError = Constraint.isInvalid();
+ if (!HasError) {
+ SubstitutedConstraintExpr =
+ cast<ConceptSpecializationExpr>(Constraint.get());
+ if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors)
+ HasError = true;
+ }
+ if (HasError) {
return new (Context) concepts::ExprRequirement(
createSubstDiagAt(IDC->getExprLoc(),
[&](llvm::raw_ostream &OS) {
@@ -8335,8 +8342,6 @@ Sema::BuildExprRequirement(
}),
IsSimple, NoexceptLoc, ReturnTypeRequirement);
}
- SubstitutedConstraintExpr =
- cast<ConceptSpecializationExpr>(Constraint.get());
if (!SubstitutedConstraintExpr->isSatisfied())
Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 41c3f81639c82..d5f23db6bacb2 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4648,13 +4648,11 @@ void Sema::diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
diagnoseMissingTemplateArguments(Name, Loc);
}
-ExprResult
-Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
- SourceLocation TemplateKWLoc,
- const DeclarationNameInfo &ConceptNameInfo,
- NamedDecl *FoundDecl,
- ConceptDecl *NamedConcept,
- const TemplateArgumentListInfo *TemplateArgs) {
+ExprResult Sema::CheckConceptTemplateId(
+ const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
+ const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl,
+ ConceptDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs,
+ bool DoCheckConstraintSatisfaction) {
assert(NamedConcept && "A concept template id without a template?");
if (NamedConcept->isInvalidDecl())
@@ -4685,19 +4683,25 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
EnterExpressionEvaluationContext EECtx{
*this, ExpressionEvaluationContext::Unevaluated, CSD};
- if (!AreArgsDependent &&
- CheckConstraintSatisfaction(
- NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
- MLTAL,
- SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
- TemplateArgs->getRAngleLoc()),
- Satisfaction))
- return ExprError();
auto *CL = ConceptReference::Create(
Context,
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept,
ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs));
+
+ bool Error = false;
+ if (!AreArgsDependent && DoCheckConstraintSatisfaction)
+ Error = CheckConstraintSatisfaction(
+ NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
+ MLTAL,
+ SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
+ TemplateArgs->getRAngleLoc()),
+ Satisfaction, CL);
+
+ if (!DoCheckConstraintSatisfaction)
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = Error;
+
return ConceptSpecializationExpr::Create(
Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction);
}
@@ -5939,7 +5943,8 @@ bool Sema::CheckTemplateArgumentList(
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConceptInstantiation=*/true);
- if (EnsureTemplateArgumentListConstraints(
+ if (!isa<ConceptDecl>(Template) &&
+ EnsureTemplateArgumentListConstraints(
Template, MLTAL,
SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
if (ConstraintsNotSatisfied)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 217d57d67f067..ab57368d0a6fc 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -7073,6 +7073,14 @@ void Sema::MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
/*OnlyDeduced=*/false, Depth, Used);
}
+void Sema::MarkUsedTemplateParameters(
+ ArrayRef<TemplateArgumentLoc> TemplateArgs, unsigned Depth,
+ llvm::SmallBitVector &Used) {
+ for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
+ ::MarkUsedTemplateParameters(Context, TemplateArgs[I].getArgument(),
+ /*OnlyDeduced=*/false, Depth, Used);
+}
+
void Sema::MarkDeducedTemplateParameters(
ASTContext &Ctx, const FunctionTemplateDecl *FunctionTemplate,
llvm::SmallBitVector &Deduced) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index cd20dfb4fe093..e94f376bdeba3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1364,516 +1364,526 @@ getPackSubstitutedTemplateArgument(Sema &S, TemplateArgument Arg) {
// Template Instantiation for Types
//===----------------------------------------------------------------------===/
namespace {
- class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
- const MultiLevelTemplateArgumentList &TemplateArgs;
- SourceLocation Loc;
- DeclarationName Entity;
- // Whether to evaluate the C++20 constraints or simply substitute into them.
- bool EvaluateConstraints = true;
- // Whether Substitution was Incomplete, that is, we tried to substitute in
- // any user provided template arguments which were null.
- bool IsIncomplete = false;
- // Whether an incomplete substituion should be treated as an error.
- bool BailOutOnIncomplete;
-
- public:
- typedef TreeTransform<TemplateInstantiator> inherited;
-
- TemplateInstantiator(Sema &SemaRef,
- const MultiLevelTemplateArgumentList &TemplateArgs,
- SourceLocation Loc, DeclarationName Entity,
- bool BailOutOnIncomplete = false)
- : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
- Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}
- void setEvaluateConstraints(bool B) {
- EvaluateConstraints = B;
- }
- bool getEvaluateConstraints() {
- return EvaluateConstraints;
- }
+class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
+ const MultiLevelTemplateArgumentList &TemplateArgs;
+ SourceLocation Loc;
+ DeclarationName Entity;
+ // Whether to evaluate the C++20 constraints or simply substitute into them.
+ bool EvaluateConstraints = true;
+ // Whether Substitution was Incomplete, that is, we tried to substitute in
+ // any user provided template arguments which were null.
+ bool IsIncomplete = false;
+ // Whether an incomplete substituion should be treated as an error.
+ bool BailOutOnIncomplete;
- /// Determine whether the given type \p T has already been
- /// transformed.
- ///
- /// For the purposes of template instantiation, a type has already been
- /// transformed if it is NULL or if it is not dependent.
- bool AlreadyTransformed(QualType T);
+ bool PreserveArgumentPacks = false;
- /// Returns the location of the entity being instantiated, if known.
- SourceLocation getBaseLocation() { return Loc; }
+public:
+ typedef TreeTransform<TemplateInstantiator> inherited;
- /// Returns the name of the entity being instantiated, if any.
- DeclarationName getBaseEntity() { return Entity; }
+ TemplateInstantiator(Sema &SemaRef,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ SourceLocation Loc, DeclarationName Entity,
+ bool BailOutOnIncomplete = false)
+ : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
+ Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}
+
+ inline static struct ForParameterMappingSubstitution_t {
+ } ForParameterMappingSubstitution;
+
+ TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
+ const MultiLevelTemplateArgumentList &TemplateArgs)
+ : inherited(SemaRef), TemplateArgs(TemplateArgs),
+ BailOutOnIncomplete(false), PreserveArgumentPacks(true) {}
+
+ void setEvaluateConstraints(bool B) { EvaluateConstraints = B; }
+ bool getEvaluateConstraints() { return EvaluateConstraints; }
+
+ /// Determine whether the given type \p T has already been
+ /// transformed.
+ ///
+ /// For the purposes of template instantiation, a type has already been
+ /// transformed if it is NULL or if it is not dependent.
+ bool AlreadyTransformed(QualType T);
+
+ /// Returns the location of the entity being instantiated, if known.
+ SourceLocation getBaseLocation() { return Loc; }
+
+ /// Returns the name of the entity being instantiated, if any.
+ DeclarationName getBaseEntity() { return Entity; }
+
+ /// Returns whether any substitution so far was incomplete.
+ bool getIsIncomplete() const { return IsIncomplete; }
+
+ /// Sets the "base" location and entity when that
+ /// information is known based on another transformation.
+ void setBase(SourceLocation Loc, DeclarationName Entity) {
+ this->Loc = Loc;
+ this->Entity = Entity;
+ }
- /// Returns whether any substitution so far was incomplete.
- bool getIsIncomplete() const { return IsIncomplete; }
+ unsigned TransformTemplateDepth(unsigned Depth) {
+ return TemplateArgs.getNewDepth(Depth);
+ }
- /// Sets the "base" location and entity when that
- /// information is known based on another transformation.
- void setBase(SourceLocation Loc, DeclarationName Entity) {
- this->Loc = Loc;
- this->Entity = Entity;
- }
+ UnsignedOrNone getPackIndex(TemplateArgument Pack) {
+ UnsignedOrNone Index = getSema().ArgPackSubstIndex;
+ if (!Index)
+ return std::nullopt;
+ return Pack.pack_size() - 1 - *Index;
+ }
- unsigned TransformTemplateDepth(unsigned Depth) {
- return TemplateArgs.getNewDepth(Depth);
- }
+ bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
+ SourceRange PatternRange,
+ ArrayRef<UnexpandedParameterPack> Unexpanded,
+ bool &ShouldExpand, bool &RetainExpansion,
+ UnsignedOrNone &NumExpansions) {
+ return getSema().CheckParameterPacksForExpansion(
+ EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
+ RetainExpansion, NumExpansions);
+ }
- UnsignedOrNone getPackIndex(TemplateArgument Pack) {
- UnsignedOrNone Index = getSema().ArgPackSubstIndex;
- if (!Index)
- return std::nullopt;
- return Pack.pack_size() - 1 - *Index;
- }
+ void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
+ SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Pack);
+ }
- bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
- SourceRange PatternRange,
- ArrayRef<UnexpandedParameterPack> Unexpanded,
- bool &ShouldExpand, bool &RetainExpansion,
- UnsignedOrNone &NumExpansions) {
- return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
- PatternRange, Unexpanded,
- TemplateArgs,
- ShouldExpand,
- RetainExpansion,
- NumExpansions);
+ TemplateArgument ForgetPartiallySubstitutedPack() {
+ TemplateArgument Result;
+ if (NamedDecl *PartialPack =
+ SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()) {
+ MultiLevelTemplateArgumentList &TemplateArgs =
+ const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+ unsigned Depth, Index;
+ std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
+ if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
+ Result = TemplateArgs(Depth, Index);
+ TemplateArgs.setArgument(Depth, Index, TemplateArgument());
+ } else {
+ IsIncomplete = true;
+ if (BailOutOnIncomplete)
+ return TemplateArgument();
+ }
}
- void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
- SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Pack);
+ return Result;
+ }
+
+ void RememberPartiallySubstitutedPack(TemplateArgument Arg) {
+ if (Arg.isNull())
+ return;
+
+ if (NamedDecl *PartialPack =
+ SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()) {
+ MultiLevelTemplateArgumentList &TemplateArgs =
+ const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+ unsigned Depth, Index;
+ std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
+ TemplateArgs.setArgument(Depth, Index, Arg);
}
+ }
- TemplateArgument ForgetPartiallySubstitutedPack() {
- TemplateArgument Result;
- if (NamedDecl *PartialPack
- = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
- MultiLevelTemplateArgumentList &TemplateArgs
- = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
- unsigned Depth, Index;
- std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
- if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
- Result = TemplateArgs(Depth, Index);
- TemplateArgs.setArgument(Depth, Index, TemplateArgument());
- } else {
- IsIncomplete = true;
- if (BailOutOnIncomplete)
- return TemplateArgument();
- }
- }
+ bool ShouldPreserveTemplateArgumentsPacks() const {
+ return PreserveArgumentPacks;
+ }
- return Result;
- }
+ TemplateArgument
+ getTemplateArgumentPackPatternForRewrite(const TemplateArgument &TA) {
+ if (TA.getKind() != TemplateArgument::Pack)
+ return TA;
+ if (SemaRef.ArgPackSubstIndex)
+ return getPackSubstitutedTemplateArgument(SemaRef, TA);
+ assert(TA.pack_size() == 1 && TA.pack_begin()->isPackExpansion() &&
+ "unexpected pack arguments in template rewrite");
+ TemplateArgument Arg = *TA.pack_begin();
+ if (Arg.isPackExpansion())
+ Arg = Arg.getPackExpansionPattern();
+ return Arg;
+ }
- void RememberPartiallySubstitutedPack(TemplateArgument Arg) {
- if (Arg.isNull())
- return;
-
- if (NamedDecl *PartialPack
- = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
- MultiLevelTemplateArgumentList &TemplateArgs
- = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
- unsigned Depth, Index;
- std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
- TemplateArgs.setArgument(Depth, Index, Arg);
- }
- }
+ /// Transform the given declaration by instantiating a reference to
+ /// this declaration.
+ Decl *TransformDecl(SourceLocation Loc, Decl *D);
- TemplateArgument
- getTemplateArgumentPackPatternForRewrite(const TemplateArgument &TA) {
- if (TA.getKind() != TemplateArgument::Pack)
- return TA;
- if (SemaRef.ArgPackSubstIndex)
- return getPackSubstitutedTemplateArgument(SemaRef, TA);
- assert(TA.pack_size() == 1 && TA.pack_begin()->isPackExpansion() &&
- "unexpected pack arguments in template rewrite");
- TemplateArgument Arg = *TA.pack_begin();
- if (Arg.isPackExpansion())
- Arg = Arg.getPackExpansionPattern();
- return Arg;
- }
+ void transformAttrs(Decl *Old, Decl *New) {
+ SemaRef.InstantiateAttrs(TemplateArgs, Old, New);
+ }
- /// Transform the given declaration by instantiating a reference to
- /// this declaration.
- Decl *TransformDecl(SourceLocation Loc, Decl *D);
+ void transformedLocalDecl(Decl *Old, ArrayRef<Decl *> NewDecls) {
+ if (Old->isParameterPack() &&
+ (NewDecls.size() != 1 || !NewDecls.front()->isParameterPack())) {
+ SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Old);
+ for (auto *New : NewDecls)
+ SemaRef.CurrentInstantiationScope->InstantiatedLocalPackArg(
+ Old, cast<VarDecl>(New));
+ return;
+ }
- void transformAttrs(Decl *Old, Decl *New) {
- SemaRef.InstantiateAttrs(TemplateArgs, Old, New);
+ assert(NewDecls.size() == 1 &&
+ "should only have multiple expansions for a pack");
+ Decl *New = NewDecls.front();
+
+ // If we've instantiated the call operator of a lambda or the call
+ // operator template of a generic lambda, update the "instantiation of"
+ // information.
+ auto *NewMD = dyn_cast<CXXMethodDecl>(New);
+ if (NewMD && isLambdaCallOperator(NewMD)) {
+ auto *OldMD = dyn_cast<CXXMethodDecl>(Old);
+ if (auto *NewTD = NewMD->getDescribedFunctionTemplate())
+ NewTD->setInstantiatedFromMemberTemplate(
+ OldMD->getDescribedFunctionTemplate());
+ else
+ NewMD->setInstantiationOfMemberFunction(OldMD,
+ TSK_ImplicitInstantiation);
}
- void transformedLocalDecl(Decl *Old, ArrayRef<Decl *> NewDecls) {
- if (Old->isParameterPack() &&
- (NewDecls.size() != 1 || !NewDecls.front()->isParameterPack())) {
- SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Old);
- for (auto *New : NewDecls)
- SemaRef.CurrentInstantiationScope->InstantiatedLocalPackArg(
- Old, cast<VarDecl>(New));
- return;
- }
+ SemaRef.CurrentInstantiationScope->InstantiatedLocal(Old, New);
- assert(NewDecls.size() == 1 &&
- "should only have multiple expansions for a pack");
- Decl *New = NewDecls.front();
-
- // If we've instantiated the call operator of a lambda or the call
- // operator template of a generic lambda, update the "instantiation of"
- // information.
- auto *NewMD = dyn_cast<CXXMethodDecl>(New);
- if (NewMD && isLambdaCallOperator(NewMD)) {
- auto *OldMD = dyn_cast<CXXMethodDecl>(Old);
- if (auto *NewTD = NewMD->getDescribedFunctionTemplate())
- NewTD->setInstantiatedFromMemberTemplate(
- OldMD->getDescribedFunctionTemplate());
- else
- NewMD->setInstantiationOfMemberFunction(OldMD,
- TSK_ImplicitInstantiation);
- }
+ // We recreated a local declaration, but not by instantiating it. There
+ // may be pending dependent diagnostics to produce.
+ if (auto *DC = dyn_cast<DeclContext>(Old);
+ DC && DC->isDependentContext() && DC->isFunctionOrMethod())
+ SemaRef.PerformDependentDiagnostics(DC, TemplateArgs);
+ }
- SemaRef.CurrentInstantiationScope->InstantiatedLocal(Old, New);
+ /// Transform the definition of the given declaration by
+ /// instantiating it.
+ Decl *TransformDefinition(SourceLocation Loc, Decl *D);
- // We recreated a local declaration, but not by instantiating it. There
- // may be pending dependent diagnostics to produce.
- if (auto *DC = dyn_cast<DeclContext>(Old);
- DC && DC->isDependentContext() && DC->isFunctionOrMethod())
- SemaRef.PerformDependentDiagnostics(DC, TemplateArgs);
- }
+ /// Transform the first qualifier within a scope by instantiating the
+ /// declaration.
+ NamedDecl *TransformFirstQualifierInScope(NamedDecl *D, SourceLocation Loc);
- /// Transform the definition of the given declaration by
- /// instantiating it.
- Decl *TransformDefinition(SourceLocation Loc, Decl *D);
-
- /// Transform the first qualifier within a scope by instantiating the
- /// declaration.
- NamedDecl *TransformFirstQualifierInScope(NamedDecl *D, SourceLocation Loc);
-
- bool TransformExceptionSpec(SourceLocation Loc,
- FunctionProtoType::ExceptionSpecInfo &ESI,
- SmallVectorImpl<QualType> &Exceptions,
- bool &Changed);
-
- /// Rebuild the exception declaration and register the declaration
- /// as an instantiated local.
- VarDecl *RebuildExceptionDecl(VarDecl *ExceptionDecl,
- TypeSourceInfo *Declarator,
- SourceLocation StartLoc,
- SourceLocation NameLoc,
- IdentifierInfo *Name);
-
- /// Rebuild the Objective-C exception declaration and register the
- /// declaration as an instantiated local.
- VarDecl *RebuildObjCExceptionDecl(VarDecl *ExceptionDecl,
- TypeSourceInfo *TSInfo, QualType T);
-
- /// Check for tag mismatches when instantiating an
- /// elaborated type.
- QualType RebuildElaboratedType(SourceLocation KeywordLoc,
- ElaboratedTypeKeyword Keyword,
- NestedNameSpecifierLoc QualifierLoc,
- QualType T);
-
- TemplateName
- TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
- SourceLocation NameLoc,
- QualType ObjectType = QualType(),
- NamedDecl *FirstQualifierInScope = nullptr,
- bool AllowInjectedClassName = false);
-
- const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA);
- const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
- const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
- const NoInlineAttr *TransformStmtNoInlineAttr(const Stmt *OrigS,
- const Stmt *InstS,
- const NoInlineAttr *A);
- const AlwaysInlineAttr *
- TransformStmtAlwaysInlineAttr(const Stmt *OrigS, const Stmt *InstS,
- const AlwaysInlineAttr *A);
- const CodeAlignAttr *TransformCodeAlignAttr(const CodeAlignAttr *CA);
- const OpenACCRoutineDeclAttr *
- TransformOpenACCRoutineDeclAttr(const OpenACCRoutineDeclAttr *A);
- ExprResult TransformPredefinedExpr(PredefinedExpr *E);
- ExprResult TransformDeclRefExpr(DeclRefExpr *E);
- ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E);
-
- ExprResult TransformTemplateParmRefExpr(DeclRefExpr *E,
- NonTypeTemplateParmDecl *D);
- ExprResult TransformSubstNonTypeTemplateParmPackExpr(
- SubstNonTypeTemplateParmPackExpr *E);
- ExprResult TransformSubstNonTypeTemplateParmExpr(
- SubstNonTypeTemplateParmExpr *E);
-
- /// Rebuild a DeclRefExpr for a VarDecl reference.
- ExprResult RebuildVarDeclRefExpr(ValueDecl *PD, SourceLocation Loc);
-
- /// Transform a reference to a function or init-capture parameter pack.
- ExprResult TransformFunctionParmPackRefExpr(DeclRefExpr *E, ValueDecl *PD);
-
- /// Transform a FunctionParmPackExpr which was built when we couldn't
- /// expand a function parameter pack reference which refers to an expanded
- /// pack.
- ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E);
-
- QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
- FunctionProtoTypeLoc TL) {
- // Call the base version; it will forward to our overridden version below.
- return inherited::TransformFunctionProtoType(TLB, TL);
- }
+ bool TransformExceptionSpec(SourceLocation Loc,
+ FunctionProtoType::ExceptionSpecInfo &ESI,
+ SmallVectorImpl<QualType> &Exceptions,
+ bool &Changed);
+
+ /// Rebuild the exception declaration and register the declaration
+ /// as an instantiated local.
+ VarDecl *RebuildExceptionDecl(VarDecl *ExceptionDecl,
+ TypeSourceInfo *Declarator,
+ SourceLocation StartLoc, SourceLocation NameLoc,
+ IdentifierInfo *Name);
+
+ /// Rebuild the Objective-C exception declaration and register the
+ /// declaration as an instantiated local.
+ VarDecl *RebuildObjCExceptionDecl(VarDecl *ExceptionDecl,
+ TypeSourceInfo *TSInfo, QualType T);
+
+ /// Check for tag mismatches when instantiating an
+ /// elaborated type.
+ QualType RebuildElaboratedType(SourceLocation KeywordLoc,
+ ElaboratedTypeKeyword Keyword,
+ NestedNameSpecifierLoc QualifierLoc,
+ QualType T);
+
+ TemplateName TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
+ SourceLocation NameLoc,
+ QualType ObjectType = QualType(),
+ NamedDecl *FirstQualifierInScope = nullptr,
+ bool AllowInjectedClassName = false);
+
+ const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA);
+ const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
+ const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
+ const NoInlineAttr *TransformStmtNoInlineAttr(const Stmt *OrigS,
+ const Stmt *InstS,
+ const NoInlineAttr *A);
+ const AlwaysInlineAttr *
+ TransformStmtAlwaysInlineAttr(const Stmt *OrigS, const Stmt *InstS,
+ const AlwaysInlineAttr *A);
+ const CodeAlignAttr *TransformCodeAlignAttr(const CodeAlignAttr *CA);
+ const OpenACCRoutineDeclAttr *
+ TransformOpenACCRoutineDeclAttr(const OpenACCRoutineDeclAttr *A);
+ ExprResult TransformPredefinedExpr(PredefinedExpr *E);
+ ExprResult TransformDeclRefExpr(DeclRefExpr *E);
+ ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E);
+
+ ExprResult TransformTemplateParmRefExpr(DeclRefExpr *E,
+ NonTypeTemplateParmDecl *D);
+ ExprResult TransformSubstNonTypeTemplateParmPackExpr(
+ SubstNonTypeTemplateParmPackExpr *E);
+ ExprResult
+ TransformSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E);
+
+ /// Rebuild a DeclRefExpr for a VarDecl reference.
+ ExprResult RebuildVarDeclRefExpr(ValueDecl *PD, SourceLocation Loc);
+
+ /// Transform a reference to a function or init-capture parameter pack.
+ ExprResult TransformFunctionParmPackRefExpr(DeclRefExpr *E, ValueDecl *PD);
+
+ /// Transform a FunctionParmPackExpr which was built when we couldn't
+ /// expand a function parameter pack reference which refers to an expanded
+ /// pack.
+ ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E);
+
+ QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
+ FunctionProtoTypeLoc TL) {
+ // Call the base version; it will forward to our overridden version below.
+ return inherited::TransformFunctionProtoType(TLB, TL);
+ }
- QualType TransformInjectedClassNameType(TypeLocBuilder &TLB,
- InjectedClassNameTypeLoc TL) {
- auto Type = inherited::TransformInjectedClassNameType(TLB, TL);
- // Special case for transforming a deduction guide, we return a
- // transformed TemplateSpecializationType.
- if (Type.isNull() &&
- SemaRef.CodeSynthesisContexts.back().Kind ==
- Sema::CodeSynthesisContext::BuildingDeductionGuides) {
- // Return a TemplateSpecializationType for transforming a deduction
- // guide.
- if (auto *ICT = TL.getType()->getAs<InjectedClassNameType>()) {
- auto Type =
- inherited::TransformType(ICT->getInjectedSpecializationType());
- TLB.pushTrivial(SemaRef.Context, Type, TL.getNameLoc());
- return Type;
- }
+ QualType TransformInjectedClassNameType(TypeLocBuilder &TLB,
+ InjectedClassNameTypeLoc TL) {
+ auto Type = inherited::TransformInjectedClassNameType(TLB, TL);
+ // Special case for transforming a deduction guide, we return a
+ // transformed TemplateSpecializationType.
+ if (Type.isNull() &&
+ SemaRef.CodeSynthesisContexts.back().Kind ==
+ Sema::CodeSynthesisContext::BuildingDeductionGuides) {
+ // Return a TemplateSpecializationType for transforming a deduction
+ // guide.
+ if (auto *ICT = TL.getType()->getAs<InjectedClassNameType>()) {
+ auto Type =
+ inherited::TransformType(ICT->getInjectedSpecializationType());
+ TLB.pushTrivial(SemaRef.Context, Type, TL.getNameLoc());
+ return Type;
}
- return Type;
}
- // Override the default version to handle a rewrite-template-arg-pack case
- // for building a deduction guide.
- bool TransformTemplateArgument(const TemplateArgumentLoc &Input,
- TemplateArgumentLoc &Output,
- bool Uneval = false) {
- const TemplateArgument &Arg = Input.getArgument();
- std::vector<TemplateArgument> TArgs;
- switch (Arg.getKind()) {
- case TemplateArgument::Pack:
- assert(SemaRef.CodeSynthesisContexts.empty() ||
- SemaRef.CodeSynthesisContexts.back().Kind ==
- Sema::CodeSynthesisContext::BuildingDeductionGuides);
- // Literally rewrite the template argument pack, instead of unpacking
- // it.
- for (auto &pack : Arg.getPackAsArray()) {
- TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
- pack, QualType(), SourceLocation{});
- TemplateArgumentLoc Output;
- if (TransformTemplateArgument(Input, Output, Uneval))
- return true; // fails
- TArgs.push_back(Output.getArgument());
- }
- Output = SemaRef.getTrivialTemplateArgumentLoc(
- TemplateArgument(llvm::ArrayRef(TArgs).copy(SemaRef.Context)),
- QualType(), SourceLocation{});
- return false;
- default:
- break;
+ return Type;
+ }
+ // Override the default version to handle a rewrite-template-arg-pack case
+ // for building a deduction guide.
+ bool TransformTemplateArgument(const TemplateArgumentLoc &Input,
+ TemplateArgumentLoc &Output,
+ bool Uneval = false) {
+ const TemplateArgument &Arg = Input.getArgument();
+ std::vector<TemplateArgument> TArgs;
+ switch (Arg.getKind()) {
+ case TemplateArgument::Pack:
+ assert(SemaRef.CodeSynthesisContexts.empty() ||
+ SemaRef.CodeSynthesisContexts.back().Kind ==
+ Sema::CodeSynthesisContext::BuildingDeductionGuides);
+ // Literally rewrite the template argument pack, instead of unpacking
+ // it.
+ for (auto &pack : Arg.getPackAsArray()) {
+ TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
+ pack, QualType(), SourceLocation{});
+ TemplateArgumentLoc Output;
+ if (TransformTemplateArgument(Input, Output, Uneval))
+ return true; // fails
+ TArgs.push_back(Output.getArgument());
}
- return inherited::TransformTemplateArgument(Input, Output, Uneval);
+ Output = SemaRef.getTrivialTemplateArgumentLoc(
+ TemplateArgument(llvm::ArrayRef(TArgs).copy(SemaRef.Context)),
+ QualType(), SourceLocation{});
+ return false;
+ default:
+ break;
}
+ return inherited::TransformTemplateArgument(Input, Output, Uneval);
+ }
- UnsignedOrNone ComputeSizeOfPackExprWithoutSubstitution(
- ArrayRef<TemplateArgument> PackArgs) {
- // Don't do this when rewriting template parameters for CTAD:
- // 1) The heuristic needs the unpacked Subst* nodes to figure out the
- // expanded size, but this never applies since Subst* nodes are not
- // created in rewrite scenarios.
- //
- // 2) The heuristic substitutes into the pattern with pack expansion
- // suppressed, which does not meet the requirements for argument
- // rewriting when template arguments include a non-pack matching against
- // a pack, particularly when rewriting an alias CTAD.
- if (TemplateArgs.isRewrite())
- return std::nullopt;
-
- return inherited::ComputeSizeOfPackExprWithoutSubstitution(PackArgs);
- }
+ UnsignedOrNone ComputeSizeOfPackExprWithoutSubstitution(
+ ArrayRef<TemplateArgument> PackArgs) {
+ // Don't do this when rewriting template parameters for CTAD:
+ // 1) The heuristic needs the unpacked Subst* nodes to figure out the
+ // expanded size, but this never applies since Subst* nodes are not
+ // created in rewrite scenarios.
+ //
+ // 2) The heuristic substitutes into the pattern with pack expansion
+ // suppressed, which does not meet the requirements for argument
+ // rewriting when template arguments include a non-pack matching against
+ // a pack, particularly when rewriting an alias CTAD.
+ if (TemplateArgs.isRewrite())
+ return std::nullopt;
- template<typename Fn>
- QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
- FunctionProtoTypeLoc TL,
- CXXRecordDecl *ThisContext,
- Qualifiers ThisTypeQuals,
- Fn TransformExceptionSpec);
-
- ParmVarDecl *TransformFunctionTypeParam(ParmVarDecl *OldParm,
- int indexAdjustment,
- UnsignedOrNone NumExpansions,
- bool ExpectParameterPack);
-
- using inherited::TransformTemplateTypeParmType;
- /// Transforms a template type parameter type by performing
- /// substitution of the corresponding template type argument.
- QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
- TemplateTypeParmTypeLoc TL,
- bool SuppressObjCLifetime);
-
- QualType BuildSubstTemplateTypeParmType(
- TypeLocBuilder &TLB, bool SuppressObjCLifetime, bool Final,
- Decl *AssociatedDecl, unsigned Index, UnsignedOrNone PackIndex,
- TemplateArgument Arg, SourceLocation NameLoc);
-
- /// Transforms an already-substituted template type parameter pack
- /// into either itself (if we aren't substituting into its pack expansion)
- /// or the appropriate substituted argument.
- using inherited::TransformSubstTemplateTypeParmPackType;
- QualType
- TransformSubstTemplateTypeParmPackType(TypeLocBuilder &TLB,
- SubstTemplateTypeParmPackTypeLoc TL,
- bool SuppressObjCLifetime);
-
- CXXRecordDecl::LambdaDependencyKind
- ComputeLambdaDependency(LambdaScopeInfo *LSI) {
- if (auto TypeAlias =
- TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
- getSema());
- TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
- LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
- unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
- if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
+ return inherited::ComputeSizeOfPackExprWithoutSubstitution(PackArgs);
+ }
+
+ template <typename Fn>
+ QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
+ FunctionProtoTypeLoc TL,
+ CXXRecordDecl *ThisContext,
+ Qualifiers ThisTypeQuals,
+ Fn TransformExceptionSpec);
+
+ ParmVarDecl *TransformFunctionTypeParam(ParmVarDecl *OldParm,
+ int indexAdjustment,
+ UnsignedOrNone NumExpansions,
+ bool ExpectParameterPack);
+
+ using inherited::TransformTemplateTypeParmType;
+ /// Transforms a template type parameter type by performing
+ /// substitution of the corresponding template type argument.
+ QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
+ TemplateTypeParmTypeLoc TL,
+ bool SuppressObjCLifetime);
+
+ QualType BuildSubstTemplateTypeParmType(TypeLocBuilder &TLB,
+ bool SuppressObjCLifetime, bool Final,
+ Decl *AssociatedDecl, unsigned Index,
+ UnsignedOrNone PackIndex,
+ TemplateArgument Arg,
+ SourceLocation NameLoc);
+
+ /// Transforms an already-substituted template type parameter pack
+ /// into either itself (if we aren't substituting into its pack expansion)
+ /// or the appropriate substituted argument.
+ using inherited::TransformSubstTemplateTypeParmPackType;
+ QualType
+ TransformSubstTemplateTypeParmPackType(TypeLocBuilder &TLB,
+ SubstTemplateTypeParmPackTypeLoc TL,
+ bool SuppressObjCLifetime);
+
+ CXXRecordDecl::LambdaDependencyKind
+ ComputeLambdaDependency(LambdaScopeInfo *LSI) {
+ if (auto TypeAlias =
+ TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
+ getSema());
+ TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
+ LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
+ unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
+ if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
+ return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
+ for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments)
+ if (TA.isDependent())
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
- for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments)
- if (TA.isDependent())
- return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
- }
- return inherited::ComputeLambdaDependency(LSI);
}
+ return inherited::ComputeLambdaDependency(LSI);
+ }
- ExprResult TransformLambdaExpr(LambdaExpr *E) {
- // Do not rebuild lambdas to avoid creating a new type.
- // Lambdas have already been processed inside their eval contexts.
- if (SemaRef.RebuildingImmediateInvocation)
- return E;
- LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
- /*InstantiatingLambdaOrBlock=*/true);
- Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
+ ExprResult TransformLambdaExpr(LambdaExpr *E) {
+ // Do not rebuild lambdas to avoid creating a new type.
+ // Lambdas have already been processed inside their eval contexts.
+ if (SemaRef.RebuildingImmediateInvocation)
+ return E;
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
+ /*InstantiatingLambdaOrBlock=*/true);
+ Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
- return inherited::TransformLambdaExpr(E);
- }
+ return inherited::TransformLambdaExpr(E);
+ }
- ExprResult TransformBlockExpr(BlockExpr *E) {
- LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
- /*InstantiatingLambdaOrBlock=*/true);
- return inherited::TransformBlockExpr(E);
- }
+ ExprResult TransformBlockExpr(BlockExpr *E) {
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
+ /*InstantiatingLambdaOrBlock=*/true);
+ return inherited::TransformBlockExpr(E);
+ }
- ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
- LambdaScopeInfo *LSI) {
- CXXMethodDecl *MD = LSI->CallOperator;
- for (ParmVarDecl *PVD : MD->parameters()) {
- assert(PVD && "null in a parameter list");
- if (!PVD->hasDefaultArg())
- continue;
- Expr *UninstExpr = PVD->getUninstantiatedDefaultArg();
- // FIXME: Obtain the source location for the '=' token.
- SourceLocation EqualLoc = UninstExpr->getBeginLoc();
- if (SemaRef.SubstDefaultArgument(EqualLoc, PVD, TemplateArgs)) {
- // If substitution fails, the default argument is set to a
- // RecoveryExpr that wraps the uninstantiated default argument so
- // that downstream diagnostics are omitted.
- ExprResult ErrorResult = SemaRef.CreateRecoveryExpr(
- UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), {UninstExpr},
- UninstExpr->getType());
- if (ErrorResult.isUsable())
- PVD->setDefaultArg(ErrorResult.get());
- }
+ ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
+ LambdaScopeInfo *LSI) {
+ CXXMethodDecl *MD = LSI->CallOperator;
+ for (ParmVarDecl *PVD : MD->parameters()) {
+ assert(PVD && "null in a parameter list");
+ if (!PVD->hasDefaultArg())
+ continue;
+ Expr *UninstExpr = PVD->getUninstantiatedDefaultArg();
+ // FIXME: Obtain the source location for the '=' token.
+ SourceLocation EqualLoc = UninstExpr->getBeginLoc();
+ if (SemaRef.SubstDefaultArgument(EqualLoc, PVD, TemplateArgs)) {
+ // If substitution fails, the default argument is set to a
+ // RecoveryExpr that wraps the uninstantiated default argument so
+ // that downstream diagnostics are omitted.
+ ExprResult ErrorResult = SemaRef.CreateRecoveryExpr(
+ UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), {UninstExpr},
+ UninstExpr->getType());
+ if (ErrorResult.isUsable())
+ PVD->setDefaultArg(ErrorResult.get());
}
- return inherited::RebuildLambdaExpr(StartLoc, EndLoc, LSI);
}
+ return inherited::RebuildLambdaExpr(StartLoc, EndLoc, LSI);
+ }
- StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
- // Currently, we instantiate the body when instantiating the lambda
- // expression. However, `EvaluateConstraints` is disabled during the
- // instantiation of the lambda expression, causing the instantiation
- // failure of the return type requirement in the body. If p0588r1 is fully
- // implemented, the body will be lazily instantiated, and this problem
- // will not occur. Here, `EvaluateConstraints` is temporarily set to
- // `true` to temporarily fix this issue.
- // FIXME: This temporary fix can be removed after fully implementing
- // p0588r1.
- llvm::SaveAndRestore _(EvaluateConstraints, true);
- return inherited::TransformLambdaBody(E, Body);
- }
+ StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
+ // Currently, we instantiate the body when instantiating the lambda
+ // expression. However, `EvaluateConstraints` is disabled during the
+ // instantiation of the lambda expression, causing the instantiation
+ // failure of the return type requirement in the body. If p0588r1 is fully
+ // implemented, the body will be lazily instantiated, and this problem
+ // will not occur. Here, `EvaluateConstraints` is temporarily set to
+ // `true` to temporarily fix this issue.
+ // FIXME: This temporary fix can be removed after fully implementing
+ // p0588r1.
+ llvm::SaveAndRestore _(EvaluateConstraints, true);
+ return inherited::TransformLambdaBody(E, Body);
+ }
- ExprResult TransformRequiresExpr(RequiresExpr *E) {
- LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
- ExprResult TransReq = inherited::TransformRequiresExpr(E);
- if (TransReq.isInvalid())
- return TransReq;
- assert(TransReq.get() != E &&
- "Do not change value of isSatisfied for the existing expression. "
- "Create a new expression instead.");
- if (E->getBody()->isDependentContext()) {
- Sema::SFINAETrap Trap(SemaRef);
- // We recreate the RequiresExpr body, but not by instantiating it.
- // Produce pending diagnostics for dependent access check.
- SemaRef.PerformDependentDiagnostics(E->getBody(), TemplateArgs);
- // FIXME: Store SFINAE diagnostics in RequiresExpr for diagnosis.
- if (Trap.hasErrorOccurred())
- TransReq.getAs<RequiresExpr>()->setSatisfied(false);
- }
+ ExprResult TransformRequiresExpr(RequiresExpr *E) {
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+ ExprResult TransReq = inherited::TransformRequiresExpr(E);
+ if (TransReq.isInvalid())
return TransReq;
+ assert(TransReq.get() != E &&
+ "Do not change value of isSatisfied for the existing expression. "
+ "Create a new expression instead.");
+ if (E->getBody()->isDependentContext()) {
+ Sema::SFINAETrap Trap(SemaRef);
+ // We recreate the RequiresExpr body, but not by instantiating it.
+ // Produce pending diagnostics for dependent access check.
+ SemaRef.PerformDependentDiagnostics(E->getBody(), TemplateArgs);
+ // FIXME: Store SFINAE diagnostics in RequiresExpr for diagnosis.
+ if (Trap.hasErrorOccurred())
+ TransReq.getAs<RequiresExpr>()->setSatisfied(false);
}
+ return TransReq;
+ }
- bool TransformRequiresExprRequirements(
- ArrayRef<concepts::Requirement *> Reqs,
- SmallVectorImpl<concepts::Requirement *> &Transformed) {
- bool SatisfactionDetermined = false;
- for (concepts::Requirement *Req : Reqs) {
- concepts::Requirement *TransReq = nullptr;
- if (!SatisfactionDetermined) {
- if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req))
- TransReq = TransformTypeRequirement(TypeReq);
- else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req))
- TransReq = TransformExprRequirement(ExprReq);
- else
- TransReq = TransformNestedRequirement(
- cast<concepts::NestedRequirement>(Req));
- if (!TransReq)
- return true;
- if (!TransReq->isDependent() && !TransReq->isSatisfied())
- // [expr.prim.req]p6
- // [...] The substitution and semantic constraint checking
- // proceeds in lexical order and stops when a condition that
- // determines the result of the requires-expression is
- // encountered. [..]
- SatisfactionDetermined = true;
- } else
- TransReq = Req;
- Transformed.push_back(TransReq);
- }
- return false;
+ bool TransformRequiresExprRequirements(
+ ArrayRef<concepts::Requirement *> Reqs,
+ SmallVectorImpl<concepts::Requirement *> &Transformed) {
+ bool SatisfactionDetermined = false;
+ for (concepts::Requirement *Req : Reqs) {
+ concepts::Requirement *TransReq = nullptr;
+ if (!SatisfactionDetermined) {
+ if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req))
+ TransReq = TransformTypeRequirement(TypeReq);
+ else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req))
+ TransReq = TransformExprRequirement(ExprReq);
+ else
+ TransReq = TransformNestedRequirement(
+ cast<concepts::NestedRequirement>(Req));
+ if (!TransReq)
+ return true;
+ if (!TransReq->isDependent() && !TransReq->isSatisfied())
+ // [expr.prim.req]p6
+ // [...] The substitution and semantic constraint checking
+ // proceeds in lexical order and stops when a condition that
+ // determines the result of the requires-expression is
+ // encountered. [..]
+ SatisfactionDetermined = true;
+ } else
+ TransReq = Req;
+ Transformed.push_back(TransReq);
}
+ return false;
+ }
- TemplateParameterList *TransformTemplateParameterList(
- TemplateParameterList *OrigTPL) {
- if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
-
- DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
- TemplateDeclInstantiator DeclInstantiator(getSema(),
- /* DeclContext *Owner */ Owner, TemplateArgs);
- DeclInstantiator.setEvaluateConstraints(EvaluateConstraints);
- return DeclInstantiator.SubstTemplateParams(OrigTPL);
- }
+ TemplateParameterList *
+ TransformTemplateParameterList(TemplateParameterList *OrigTPL) {
+ if (!OrigTPL || !OrigTPL->size())
+ return OrigTPL;
+
+ DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
+ TemplateDeclInstantiator DeclInstantiator(getSema(),
+ /* DeclContext *Owner */ Owner,
+ TemplateArgs);
+ DeclInstantiator.setEvaluateConstraints(EvaluateConstraints);
+ return DeclInstantiator.SubstTemplateParams(OrigTPL);
+ }
- concepts::TypeRequirement *
- TransformTypeRequirement(concepts::TypeRequirement *Req);
- concepts::ExprRequirement *
- TransformExprRequirement(concepts::ExprRequirement *Req);
- concepts::NestedRequirement *
- TransformNestedRequirement(concepts::NestedRequirement *Req);
- ExprResult TransformRequiresTypeParams(
- SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
- RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> Params,
- SmallVectorImpl<QualType> &PTypes,
- SmallVectorImpl<ParmVarDecl *> &TransParams,
- Sema::ExtParameterInfoBuilder &PInfos);
-
- private:
- ExprResult
- transformNonTypeTemplateParmRef(Decl *AssociatedDecl,
- const NonTypeTemplateParmDecl *parm,
- SourceLocation loc, TemplateArgument arg,
- UnsignedOrNone PackIndex, bool Final);
- };
-}
+ concepts::TypeRequirement *
+ TransformTypeRequirement(concepts::TypeRequirement *Req);
+ concepts::ExprRequirement *
+ TransformExprRequirement(concepts::ExprRequirement *Req);
+ concepts::NestedRequirement *
+ TransformNestedRequirement(concepts::NestedRequirement *Req);
+ ExprResult TransformRequiresTypeParams(
+ SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
+ RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> Params,
+ SmallVectorImpl<QualType> &PTypes,
+ SmallVectorImpl<ParmVarDecl *> &TransParams,
+ Sema::ExtParameterInfoBuilder &PInfos);
+
+private:
+ ExprResult
+ transformNonTypeTemplateParmRef(Decl *AssociatedDecl,
+ const NonTypeTemplateParmDecl *parm,
+ SourceLocation loc, TemplateArgument arg,
+ UnsignedOrNone PackIndex, bool Final);
+};
+} // namespace
bool TemplateInstantiator::AlreadyTransformed(QualType T) {
if (T.isNull())
@@ -2829,69 +2839,76 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
concepts::NestedRequirement *
TemplateInstantiator::TransformNestedRequirement(
concepts::NestedRequirement *Req) {
- if (!Req->isDependent() && !AlwaysRebuild())
- return Req;
+
+ ASTContext &C = SemaRef.Context;
+
+ Expr *Constraint = Req->getConstraintExpr();
+ ExprResult TransConstraint = Constraint;
+ ConstraintSatisfaction Satisfaction;
+
+ auto NestedReqWithDiag = [&C, this](Expr *E,
+ ConstraintSatisfaction Satisfaction) {
+ Satisfaction.IsSatisfied = false;
+ SmallString<128> Entity;
+ llvm::raw_svector_ostream OS(Entity);
+ E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
+ return new (C) concepts::NestedRequirement(
+ SemaRef.Context, C.backupStr(Entity), std::move(Satisfaction));
+ };
+
if (Req->hasInvalidConstraint()) {
if (AlwaysRebuild())
return RebuildNestedRequirement(Req->getInvalidConstraintEntity(),
Req->getConstraintSatisfaction());
return Req;
}
- Sema::InstantiatingTemplate ReqInst(SemaRef,
- Req->getConstraintExpr()->getBeginLoc(), Req,
- Sema::InstantiatingTemplate::ConstraintsCheck{},
- Req->getConstraintExpr()->getSourceRange());
- if (!getEvaluateConstraints()) {
- ExprResult TransConstraint = TransformExpr(Req->getConstraintExpr());
- if (TransConstraint.isInvalid() || !TransConstraint.get())
- return nullptr;
- if (TransConstraint.get()->isInstantiationDependent())
- return new (SemaRef.Context)
- concepts::NestedRequirement(TransConstraint.get());
- ConstraintSatisfaction Satisfaction;
- return new (SemaRef.Context) concepts::NestedRequirement(
- SemaRef.Context, TransConstraint.get(), Satisfaction);
+
+ if (Req->isDependent() || AlwaysRebuild()) {
+ Sema::InstantiatingTemplate ReqInst(
+ SemaRef, Constraint->getBeginLoc(), Req,
+ Sema::InstantiatingTemplate::ConstraintsCheck{},
+ Constraint->getSourceRange());
+
+ Sema::SFINAETrap Trap(SemaRef);
+
+ TransConstraint = TransformExpr(const_cast<Expr *>(Constraint));
+ if (!TransConstraint.isUsable() || Trap.hasErrorOccurred()) {
+ return NestedReqWithDiag(Constraint, std::move(Satisfaction));
+ }
+ Constraint = TransConstraint.get();
}
- ExprResult TransConstraint;
- ConstraintSatisfaction Satisfaction;
- TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc());
+ if (Constraint->isInstantiationDependent())
+ return new (C) concepts::NestedRequirement(Constraint);
+
+ bool Success;
+ TemplateDeductionInfo Info(Constraint->getBeginLoc());
{
EnterExpressionEvaluationContext ContextRAII(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
- Sema::SFINAETrap Trap(SemaRef);
- Sema::InstantiatingTemplate ConstrInst(SemaRef,
- Req->getConstraintExpr()->getBeginLoc(), Req, Info,
- Req->getConstraintExpr()->getSourceRange());
+
+ Sema::InstantiatingTemplate ConstrInst(SemaRef, Constraint->getBeginLoc(),
+ Req, Info,
+ Constraint->getSourceRange());
if (ConstrInst.isInvalid())
return nullptr;
- llvm::SmallVector<Expr *> Result;
- if (!SemaRef.CheckConstraintSatisfaction(
- nullptr,
- AssociatedConstraint(Req->getConstraintExpr(),
- SemaRef.ArgPackSubstIndex),
- Result, TemplateArgs, Req->getConstraintExpr()->getSourceRange(),
- Satisfaction) &&
- !Result.empty())
- TransConstraint = Result[0];
- assert(!Trap.hasErrorOccurred() && "Substitution failures must be handled "
- "by CheckConstraintSatisfaction.");
- }
- ASTContext &C = SemaRef.Context;
- if (TransConstraint.isUsable() &&
- TransConstraint.get()->isInstantiationDependent())
- return new (C) concepts::NestedRequirement(TransConstraint.get());
- if (TransConstraint.isInvalid() || !TransConstraint.get() ||
- Satisfaction.HasSubstitutionFailure()) {
- SmallString<128> Entity;
- llvm::raw_svector_ostream OS(Entity);
- Req->getConstraintExpr()->printPretty(OS, nullptr,
- SemaRef.getPrintingPolicy());
- return new (C) concepts::NestedRequirement(
- SemaRef.Context, C.backupStr(Entity), Satisfaction);
+
+ Sema::SFINAETrap Trap(SemaRef);
+
+ Success = !SemaRef.CheckConstraintSatisfaction(
+ Req, AssociatedConstraint(Constraint, SemaRef.ArgPackSubstIndex),
+ TemplateArgs, Constraint->getSourceRange(), Satisfaction);
+
+ assert(!Success || !Trap.hasErrorOccurred() &&
+ "Substitution failures must be handled "
+ "by CheckConstraintSatisfaction.");
}
- return new (C)
- concepts::NestedRequirement(C, TransConstraint.get(), Satisfaction);
+
+ if (!Success || Satisfaction.HasSubstitutionFailure())
+ return NestedReqWithDiag(Constraint, Satisfaction);
+
+ // FIXME: const correctness
+ return new (C) concepts::NestedRequirement(C, Constraint, Satisfaction);
}
TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
@@ -4394,6 +4411,16 @@ bool Sema::SubstTemplateArguments(
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
}
+bool Sema::SubstTemplateArgumentsInParameterMapping(
+ ArrayRef<TemplateArgumentLoc> Args,
+ const MultiLevelTemplateArgumentList &TemplateArgs,
+ TemplateArgumentListInfo &Out) {
+ TemplateInstantiator Instantiator(
+ TemplateInstantiator::ForParameterMappingSubstitution, *this,
+ TemplateArgs);
+ return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
+}
+
ExprResult
Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
if (!E)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 5f0e968ff18c4..58cb41cbcbbcb 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -823,6 +823,8 @@ bool Sema::CheckParameterPacksForExpansion(
continue;
}
+ // TemplateArgs(Depth, Index).dump();
+
// Determine the size of the argument pack.
ArrayRef<TemplateArgument> Pack =
TemplateArgs(Depth, Index).getPackAsArray();
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 335e21d927b76..6e5fa67dfa67c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -30,6 +30,7 @@
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/AST/StmtSYCL.h"
+#include "clang/AST/TemplateBase.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/OpenMPKinds.h"
#include "clang/Sema/Designator.h"
@@ -46,8 +47,11 @@
#include "clang/Sema/SemaPseudoObject.h"
#include "clang/Sema/SemaSYCL.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>
+#include <iterator>
#include <optional>
using namespace llvm::omp;
@@ -318,6 +322,8 @@ class TreeTransform {
/// being expanded.
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { }
+ bool ShouldPreserveTemplateArgumentsPacks() const { return false; }
+
/// Transforms the given type into another type.
///
/// By default, this routine transforms a type by creating a
@@ -656,6 +662,10 @@ class TreeTransform {
TemplateArgumentListInfo &Outputs,
bool Uneval = false);
+ bool TransformTemplateArguments(ArrayRef<TemplateArgument> Args,
+ SmallVector<TemplateArgument> &Outputs,
+ bool Uneval = false);
+
/// Fakes up a TemplateArgumentLoc for a given TemplateArgument.
void InventTemplateArgumentLoc(const TemplateArgument &Arg,
TemplateArgumentLoc &ArgLoc);
@@ -5043,13 +5053,32 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
typedef TemplateArgumentLocInventIterator<Derived,
TemplateArgument::pack_iterator>
PackLocIterator;
- if (TransformTemplateArguments(PackLocIterator(*this,
- In.getArgument().pack_begin()),
- PackLocIterator(*this,
- In.getArgument().pack_end()),
- Outputs, Uneval))
+
+ TemplateArgumentListInfo *PackOutput = &Outputs;
+ TemplateArgumentListInfo New;
+
+ if (getDerived().ShouldPreserveTemplateArgumentsPacks()) {
+ PackOutput = &New;
+ }
+
+ if (TransformTemplateArguments(
+ PackLocIterator(*this, In.getArgument().pack_begin()),
+ PackLocIterator(*this, In.getArgument().pack_end()), *PackOutput,
+ Uneval))
return true;
+ if (getDerived().ShouldPreserveTemplateArgumentsPacks()) {
+ SmallVector<TemplateArgument> Args;
+ Args.reserve(New.size());
+ llvm::transform(
+ New.arguments(), std::back_inserter(Args),
+ [](const TemplateArgumentLoc &Loc) { return Loc.getArgument(); });
+ TemplateArgumentLoc ArgLoc;
+ TemplateArgument Arg =
+ TemplateArgument::CreatePackCopy(SemaRef.getASTContext(), Args);
+ InventTemplateArgumentLoc(Arg, ArgLoc);
+ Outputs.addArgument(ArgLoc);
+ }
continue;
}
@@ -5142,7 +5171,6 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
}
return false;
-
}
//===----------------------------------------------------------------------===//
@@ -6161,7 +6189,7 @@ ParmVarDecl *TreeTransform<Derived>::TransformFunctionTypeParam(
/* DefArg */ nullptr);
newParm->setScopeInfo(OldParm->getFunctionScopeDepth(),
OldParm->getFunctionScopeIndex() + indexAdjustment);
- transformedLocalDecl(OldParm, {newParm});
+ getDerived().transformedLocalDecl(OldParm, {newParm});
return newParm;
}
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index b9eabd5ddb64c..a00aa913fa440 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprOpenMP.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Lex/Token.h"
@@ -475,10 +476,10 @@ addConstraintSatisfaction(ASTRecordWriter &Record,
if (!Satisfaction.IsSatisfied) {
Record.push_back(Satisfaction.NumRecords);
for (const auto &DetailRecord : Satisfaction) {
- auto *E = dyn_cast<Expr *>(DetailRecord);
+ auto *E = dyn_cast<const Expr *>(DetailRecord);
Record.push_back(/* IsDiagnostic */ E == nullptr);
if (E)
- Record.AddStmt(E);
+ Record.AddStmt(const_cast<Expr *>(E));
else {
auto *Diag = cast<std::pair<SourceLocation, StringRef> *>(DetailRecord);
Record.AddSourceLocation(Diag->first);
diff --git a/clang/test/CXX/drs/cwg25xx.cpp b/clang/test/CXX/drs/cwg25xx.cpp
index d9a7d2bbb2671..f85ef219a93f7 100644
--- a/clang/test/CXX/drs/cwg25xx.cpp
+++ b/clang/test/CXX/drs/cwg25xx.cpp
@@ -223,15 +223,16 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
// since-cxx20-note@#cwg2565-VC {{because 'b' would be invalid: argument may not have 'void' type}}
template<typename T>
- concept ErrorRequires = requires (ErrorRequires auto x) {
+ concept ErrorRequires = requires (ErrorRequires auto x) { // #cwg2565-expr
// since-cxx20-error at -1 {{a concept definition cannot refer to itself}}
// since-cxx20-note at -2 {{declared here}}
// since-cxx20-error at -3 {{'auto' not allowed in requires expression parameter}}
x;
};
static_assert(ErrorRequires<int>);
- // since-cxx20-error at -1 {{static assertion failed}}
- // since-cxx20-note at -2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // since-cxx20-error at -1 {{static assertion failed}} \
+ // since-cxx20-note at -1 {{because 'int' does not satisfy 'ErrorRequires'}} \
+ // since-cxx20-note@#cwg2565-expr {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
template<typename T>
concept NestedErrorInRequires = requires (T x) { // #cwg2565-NEIR
@@ -243,8 +244,9 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
};
};
static_assert(NestedErrorInRequires<int>);
- // since-cxx20-error at -1 {{static assertion failed}}
- // since-cxx20-note at -2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // since-cxx20-error at -1 {{static assertion failed}} \
+ // since-cxx20-note at -1 {{because 'int' does not satisfy 'NestedErrorInRequires'}} \
+ // since-cxx20-note@#cwg2565-NEIR {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
#endif
} // namespace cwg2565
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
index 28b5d0adcf054..f4a54535524f2 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
@@ -140,7 +140,9 @@ concept C7 = sizeof(T) == 1 || sizeof(
::type) == 1;
static_assert(!C6<short>);
-static_assert(!C6<char>); // expected-note{{while checking the satisfaction of concept 'C6<char>' requested here}}
+static_assert(!C6<char>);
+// expected-error at -1 {{static assertion failed due to requirement '!C6<char>'}} \
+// expected-note at -1 {{while checking the satisfaction of concept 'C6<char>' requested here}}
static_assert(C7<char>);
static_assert(!C7<short>); // expected-note{{while checking the satisfaction of concept 'C7<short>' requested here}}
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
index 083e743818121..ef495421874e8 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
@@ -29,8 +29,7 @@ template<typename T> requires requires {
// expected-error at -1{{atomic constraint must be of type 'bool' (found 'S<int>')}}
// expected-note at -2{{while checking the satisfaction}}
// expected-note at -3{{in instantiation of requirement}}
- // expected-note at -4{{while checking the satisfaction}}
- // expected-note at -6{{while substituting template arguments}}
+ // expected-note at -5{{while substituting template arguments}}
// expected-note@#F3INST{{while checking constraint satisfaction}}
// expected-note@#F3INST{{in instantiation of function template specialization}}
//
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp
index 3992835c44402..78f94f15eff05 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp
@@ -8,9 +8,9 @@ template<typename T> requires Bar<T> && true struct S<T> { };
template<typename T> concept True2 = sizeof(T) >= 0;
template<typename T> concept Foo2 = True2<T*>;
-// expected-error at -1{{'type name' declared as a pointer to a reference of type 'type-parameter-0-0 &'}}
+// expected-error at -1 3{{'type name' declared as a pointer to a reference of type 'type-parameter-0-0 &'}}
template<typename T> concept Bar2 = Foo2<T&>;
-// expected-note at -1{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}}
+// expected-note at -1 3{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}}
template<typename T> requires Bar2<T> struct S2 { };
// expected-note at -1{{template is declared here}}
template<typename T> requires Bar2<T> && true struct S2<T> { };
@@ -71,13 +71,13 @@ namespace non_type_pack {
namespace PR47174 {
// This checks that we don't crash with a failed substitution on the first constrained argument when
// performing normalization.
-template <Bar2 T, True U>
+template <Bar2 T, True U> // expected-note {{while calculating associated constraint of template 'S3' here}}
requires true struct S3; // expected-note {{template is declared here}}
template <True T, True U>
requires true struct S3<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}}
// Same as above, for the second position (but this was already working).
-template <True T, Bar2 U>
+template <True T, Bar2 U> // expected-note {{while calculating associated constraint of template 'S4' here}}
requires true struct S4; // expected-note {{template is declared here}}
template <True T, True U>
requires true struct S4<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}}
@@ -96,7 +96,7 @@ template<class T> requires C1<T> && C2<T> void t1() = delete; // expected-note {
template void t1<X>();
void t1() { t1<X>(); } // expected-error {{call to deleted function 't1'}}
-template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}}
+template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}}
template<class T> requires C2<T> void t2() {}; // expected-note 2 {{candidate function}}
template void t2<X>(); // expected-error {{partial ordering for explicit instantiation of 't2' is ambiguous}}
void t2() { t2<X>(); } // expected-error {{call to 't2' is ambiguous}}
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index 7f80cdfe7d452..ea5ea71e555d3 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -119,13 +119,13 @@ struct F {
template <typename T>
constexpr int f5() requires C<T> { return 1; } // expected-note {{while checking the satisfaction}}
- // expected-note at -1 {{while substituting template arguments}}
- // expected-note at -2 {{candidate template ignored}}
+ // expected-note at -1 {{candidate template ignored}}
template <typename T>
-constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}}
- // expected-note at -1 4 {{while substituting template arguments}}
- // expected-note at -2 {{candidate template ignored}}
+constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}} \
+ // expected-note 4 {{while substituting template arguments}} \
+ // expected-note {{candidate template ignored}} \
+ // expected-note {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
static_assert(f5<int>() == 1);
static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint satisfaction}}
diff --git a/clang/test/SemaTemplate/concepts-recovery-expr.cpp b/clang/test/SemaTemplate/concepts-recovery-expr.cpp
index b338f3bc271bf..4636f0d883c1b 100644
--- a/clang/test/SemaTemplate/concepts-recovery-expr.cpp
+++ b/clang/test/SemaTemplate/concepts-recovery-expr.cpp
@@ -4,7 +4,7 @@
constexpr bool CausesRecoveryExpr = b;
template<typename T>
-concept ReferencesCRE = CausesRecoveryExpr;
+concept ReferencesCRE = CausesRecoveryExpr; // #subst1
template<typename T> requires CausesRecoveryExpr // #NVC1REQ
void NoViableCands1(){} // #NVC1
@@ -19,16 +19,18 @@ void NVCUse() {
NoViableCands1<int>();
// expected-error at -1 {{no matching function for call to 'NoViableCands1'}}
// expected-note@#NVC1{{candidate template ignored: constraints not satisfied}}
+ // expected-note@#NVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
// expected-note@#NVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
NoViableCands2<int>();
// expected-error at -1 {{no matching function for call to 'NoViableCands2'}}
// expected-note@#NVC2{{candidate template ignored: constraints not satisfied}}
- // expected-note@#NVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
NoViableCands3<int>();
// expected-error at -1 {{no matching function for call to 'NoViableCands3'}}
// expected-note@#NVC3{{candidate template ignored: constraints not satisfied}}
- // expected-note@#NVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#NVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
template<typename T> requires CausesRecoveryExpr // #OVC1REQ
@@ -58,12 +60,14 @@ void OVCUse() {
// expected-error at -1 {{no matching function for call to 'OtherViableCands2'}}
// expected-note@#OVC2_ALT {{candidate function}}
// expected-note@#OVC2 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#OVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#OVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
OtherViableCands3<int>();
// expected-error at -1 {{no matching function for call to 'OtherViableCands3'}}
// expected-note@#OVC3_ALT {{candidate function}}
// expected-note@#OVC3 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#OVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#OVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
template<typename T> requires CausesRecoveryExpr // #OBNVC1REQ
@@ -95,13 +99,15 @@ void OBNVCUse() {
// expected-note@#OBNVC2_ALT {{candidate template ignored: constraints not satisfied}}
// expected-note@#OBNVC2REQ_ALT {{because 'false' evaluated to false}}
// expected-note@#OBNVC2 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#OBNVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#OBNVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
OtherBadNoViableCands3<int>();
// expected-error at -1 {{no matching function for call to 'OtherBadNoViableCands3'}}
// expected-note@#OBNVC3_ALT {{candidate template ignored: constraints not satisfied}}
// expected-note@#OBNVC3REQ_ALT {{because 'false' evaluated to false}}
// expected-note@#OBNVC3 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#OBNVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#OBNVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
@@ -136,12 +142,14 @@ void MemOVCUse() {
// expected-error at -1 {{no matching member function for call to 'OtherViableCands2'}}
// expected-note@#MEMOVC2_ALT {{candidate function}}
// expected-note@#MEMOVC2 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#MEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#MEMOVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
S.OtherViableCands3<int>();
// expected-error at -1 {{no matching member function for call to 'OtherViableCands3'}}
// expected-note@#MEMOVC3_ALT {{candidate function}}
// expected-note@#MEMOVC3 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#MEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#MEMOVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
struct StaticOVC {
@@ -173,12 +181,14 @@ void StaticMemOVCUse() {
// expected-error at -1 {{no matching function for call to 'OtherViableCands2'}}
// expected-note@#SMEMOVC2_ALT {{candidate function}}
// expected-note@#SMEMOVC2 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#SMEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#SMEMOVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
StaticOVC::OtherViableCands3<int>();
// expected-error at -1 {{no matching function for call to 'OtherViableCands3'}}
// expected-note@#SMEMOVC3_ALT {{candidate function}}
// expected-note@#SMEMOVC3 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#SMEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#SMEMOVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+ // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
}
namespace GH58548 {
diff --git a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
index 3edf243982958..6f25c78346e08 100644
--- a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
+++ b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
@@ -8,7 +8,6 @@ constexpr bool is_same_v<T, T> = true;
template<typename T, typename U>
concept same_as = is_same_v<T, U>;
-// expected-note at -1{{because 'is_same_v<int, bool>' evaluated to false}}
template<typename T, typename... Us>
concept either = (is_same_v<T, Us> || ...);
>From e2794910d365888cd9a784dcc56e64ef10fdf917 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 30 May 2025 14:46:11 +0200
Subject: [PATCH 2/2] Fix handlind of concept-id which appear as an atomic
constraint
---
clang/include/clang/AST/ASTConcept.h | 27 ++-
clang/lib/AST/ASTConcept.cpp | 16 ++
clang/lib/Sema/SemaConcept.cpp | 196 ++++++++----------
...overload-resolution-deferred-templates.cpp | 3 +-
4 files changed, 122 insertions(+), 120 deletions(-)
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index c98dd4629abfc..2064702e0cb06 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -32,6 +32,14 @@ class Expr;
class NamedDecl;
struct PrintingPolicy;
+/// Pairs of unsatisfied atomic constraint expressions along with the
+/// substituted constraint expr, if the template arguments could be
+/// substituted into them, or a diagnostic if substitution resulted in
+/// an invalid expression.
+using UnsatisfiedConstraintRecord =
+ llvm::PointerUnion<const Expr *, const ConceptReference *,
+ std::pair<SourceLocation, StringRef> *>;
+
/// The result of a constraint satisfaction check, containing the necessary
/// information to diagnose an unsatisfied constraint.
class ConstraintSatisfaction : public llvm::FoldingSetNode {
@@ -50,7 +58,6 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
: ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {}
using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
- using Detail = llvm::PointerUnion<const Expr *, SubstitutionDiagnostic *>;
bool IsSatisfied = false;
bool ContainsErrors = false;
@@ -58,7 +65,7 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
/// \brief The substituted constraint expr, if the template arguments could be
/// substituted into them, or a diagnostic if substitution resulted in an
/// invalid expression.
- llvm::SmallVector<Detail, 4> Details;
+ llvm::SmallVector<UnsatisfiedConstraintRecord, 4> Details;
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) {
Profile(ID, C, ConstraintOwner, TemplateArgs);
@@ -76,13 +83,6 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
}
};
-/// Pairs of unsatisfied atomic constraint expressions along with the
-/// substituted constraint expr, if the template arguments could be
-/// substituted into them, or a diagnostic if substitution resulted in
-/// an invalid expression.
-using UnsatisfiedConstraintRecord =
- llvm::PointerUnion<const Expr *, std::pair<SourceLocation, StringRef> *>;
-
/// \brief The result of a constraint satisfaction check, containing the
/// necessary information to diagnose an unsatisfied constraint.
///
@@ -102,6 +102,10 @@ struct ASTConstraintSatisfaction final :
return getTrailingObjects() + NumRecords;
}
+ ArrayRef<UnsatisfiedConstraintRecord> records() const {
+ return {begin(), end()};
+ }
+
ASTConstraintSatisfaction(const ASTContext &C,
const ConstraintSatisfaction &Satisfaction);
ASTConstraintSatisfaction(const ASTContext &C,
@@ -287,6 +291,11 @@ class TypeConstraint {
}
};
+/// Insertion operator for diagnostics. This allows sending TemplateName's
+/// into a diagnostic with <<.
+const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
+ const ConceptReference *C);
+
} // clang
#endif // LLVM_CLANG_AST_ASTCONCEPT_H
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index d4dfc75fd25d5..f0e4d442e40e3 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -27,6 +27,9 @@ CreateUnsatisfiedConstraintRecord(const ASTContext &C,
new (TrailingObject) UnsatisfiedConstraintRecord(nullptr);
else if (const auto *E = llvm::dyn_cast<const Expr *>(Detail))
new (TrailingObject) UnsatisfiedConstraintRecord(E);
+ else if (const auto *Concept =
+ llvm::dyn_cast<const ConceptReference *>(Detail))
+ new (TrailingObject) UnsatisfiedConstraintRecord(Concept);
else {
auto &SubstitutionDiagnostic =
*cast<std::pair<SourceLocation, StringRef> *>(Detail);
@@ -112,6 +115,19 @@ void ConceptReference::print(llvm::raw_ostream &OS,
}
}
+const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
+ const ConceptReference *C) {
+ std::string NameStr;
+ llvm::raw_string_ostream OS(NameStr);
+ LangOptions LO;
+ LO.CPlusPlus = true;
+ LO.Bool = true;
+ OS << '\'';
+ C->print(OS, PrintingPolicy(LO));
+ OS << '\'';
+ return DB << NameStr;
+}
+
concepts::ExprRequirement::ExprRequirement(
Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
ReturnTypeRequirement Req, SatisfactionStatus Status,
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 7c8d80ba4ca34..22e8ba5b0eef3 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -185,27 +185,20 @@ struct SatisfactionStackRAII {
};
} // namespace
-static bool
-DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
- const NamedDecl *Templ, const Expr *E,
- const MultiLevelTemplateArgumentList &MLTAL) {
+static bool DiagRecursiveConstraintEval(
+ Sema &S, llvm::FoldingSetNodeID &ID, const NamedDecl *Templ, const Expr *E,
+ const MultiLevelTemplateArgumentList *MLTAL = nullptr) {
E->Profile(ID, S.Context, /*Canonical=*/true);
- for (const auto &List : MLTAL)
- for (const auto &TemplateArg : List.Args)
- TemplateArg.Profile(ID, S.Context);
-
- // Note that we have to do this with our own collection, because there are
- // times where a constraint-expression check can cause us to need to evaluate
- // other constriants that are unrelated, such as when evaluating a recovery
- // expression, or when trying to determine the constexpr-ness of special
- // members. Otherwise we could just use the
- // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function.
+ if (MLTAL) {
+ for (const auto &List : *MLTAL)
+ for (const auto &TemplateArg : List.Args)
+ TemplateArg.Profile(ID, S.Context);
+ }
if (S.SatisfactionStackContains(Templ, ID)) {
S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self)
<< const_cast<Expr *>(E) << E->getSourceRange();
return true;
}
-
return false;
}
@@ -261,6 +254,15 @@ static ExprResult EvaluateAtomicConstraint(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
Sema::ReuseLambdaContextDecl);
+ llvm::FoldingSetNodeID ID;
+ if (Template &&
+ DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, &MLTAL)) {
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = true;
+ return ExprEmpty();
+ }
+ SatisfactionStackRAII StackRAII(S, Template, ID);
+
// Atomic constraint - substitute arguments and check satisfaction.
ExprResult SubstitutedExpression = const_cast<Expr *>(AtomicExpr);
{
@@ -273,17 +275,6 @@ static ExprResult EvaluateAtomicConstraint(
if (Inst.isInvalid())
return ExprError();
- /*llvm::FoldingSetNodeID ID;
- if (Template &&
- DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
- Satisfaction.IsSatisfied = false;
- Satisfaction.ContainsErrors = true;
- return ExprEmpty();
- }
-
- SatisfactionStackRAII StackRAII(S, Template, ID);
- */
-
// We do not want error diagnostics escaping here.
Sema::SFINAETrap Trap(S);
@@ -621,7 +612,9 @@ static bool calculateConstraintSatisfaction(
Satisfaction.Details.insert(
Satisfaction.Details.begin() + Size,
- ConstraintSatisfaction::Detail(SubstitutedConceptId.get()));
+ UnsatisfiedConstraintRecord(
+ SubstitutedConceptId.getAs<ConceptSpecializationExpr>()
+ ->getConceptReference()));
Satisfaction.Details.push_back(nullptr);
}
@@ -1266,57 +1259,56 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
}
}
-static void
-diagnoseUnsatisfiedConceptIdExpr(Sema &S, const ConceptSpecializationExpr *CSE,
- SourceLocation Loc, bool First) {
- if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
+static void diagnoseUnsatisfiedConceptIdExpr(Sema &S,
+ const ConceptReference *Concept,
+ SourceLocation Loc, bool First) {
+ if (Concept->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
S.Diag(
Loc,
diag::
note_single_arg_concept_specialization_constraint_evaluated_to_false)
<< (int)First
- << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
- << CSE->getNamedConcept();
+ << Concept->getTemplateArgsAsWritten()->arguments()[0].getArgument()
+ << Concept->getNamedConcept();
} else {
S.Diag(Loc, diag::note_concept_specialization_constraint_evaluated_to_false)
- << (int)First << CSE;
+ << (int)First << Concept;
}
}
-static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
- const Expr *SubstExpr,
- bool First = true);
+static void diagnoseUnsatisfiedConstraintExpr(
+ Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc,
+ bool First, concepts::NestedRequirement *Req = nullptr);
-static void diagnoseUnsatisfiedRequirement(Sema &S,
- concepts::NestedRequirement *Req,
- bool First) {
- using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
+static void DiagnoseUnsatisfiedConstraint(
+ Sema &S, ArrayRef<UnsatisfiedConstraintRecord> Records, SourceLocation Loc,
+ bool First = true, concepts::NestedRequirement *Req = nullptr) {
std::vector<bool> Prevs;
- for (auto &Record : Req->getConstraintSatisfaction()) {
- if (auto *SubstDiag = Record.dyn_cast<SubstitutionDiagnostic *>())
- S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error)
- << (int)First << Req->getInvalidConstraintEntity()
- << SubstDiag->second;
- else {
- if (Record.isNull()) {
- First = Prevs.back();
- Prevs.pop_back();
- continue;
- }
- diagnoseWellFormedUnsatisfiedConstraintExpr(
- S, Record.dyn_cast<const Expr *>(), First);
- if (isa_and_nonnull<ConceptSpecializationExpr>(
- Record.dyn_cast<const Expr *>())) {
- Prevs.push_back(First);
- First = true;
- continue;
- }
- First = false;
+ for (auto &Record : Records) {
+ if (Record.isNull()) {
+ First = Prevs.back();
+ Prevs.pop_back();
+ continue;
+ }
+ diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req);
+ Loc = {};
+ if (isa<const ConceptReference *>(Record)) {
+ Prevs.push_back(First);
+ First = true;
+ continue;
}
First = false;
}
}
+static void diagnoseUnsatisfiedRequirement(Sema &S,
+ concepts::NestedRequirement *Req,
+ bool First) {
+ DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(),
+ Req->getConstraintExpr()->getExprLoc(), First,
+ Req);
+}
+
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
const Expr *SubstExpr,
bool First) {
@@ -1396,6 +1388,10 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
break;
}
return;
+ } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
+ // Drill down concept ids treated as atomic constraints
+ S.DiagnoseUnsatisfiedConstraint(CSE);
+ return;
} else if (auto *TTE = dyn_cast<TypeTraitExpr>(SubstExpr);
TTE && TTE->getTrait() == clang::TypeTrait::BTT_IsDeducible) {
assert(TTE->getNumArgs() == 2);
@@ -1411,74 +1407,49 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
S.DiagnoseTypeTraitDetails(SubstExpr);
}
-static void
-diagnoseUnsatisfiedConstraintExpr(Sema &S,
- const ConstraintSatisfaction::Detail &Record,
- SourceLocation Loc, bool First = true) {
+static void diagnoseUnsatisfiedConstraintExpr(
+ Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc,
+ bool First, concepts::NestedRequirement *Req) {
if (auto *Diag = Record.template dyn_cast<
ConstraintSatisfaction::SubstitutionDiagnostic *>()) {
- S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
- << Diag->second;
+ if (Req)
+ S.Diag(Diag->first, diag::note_nested_requirement_substitution_error)
+ << (int)First << Req->getInvalidConstraintEntity() << Diag->second;
+ else
+ S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
+ << Diag->second;
return;
}
-
- const auto *Expr = cast<const class Expr *>(Record);
- if (const auto *CSE = dyn_cast<ConceptSpecializationExpr>(Expr)) {
+ if (const auto *Concept = dyn_cast<const ConceptReference *>(Record)) {
if (Loc.isInvalid())
- Loc = CSE->getBeginLoc();
- diagnoseUnsatisfiedConceptIdExpr(S, CSE, Loc, First);
+ Loc = Concept->getBeginLoc();
+ diagnoseUnsatisfiedConceptIdExpr(S, Concept, Loc, First);
return;
}
- diagnoseWellFormedUnsatisfiedConstraintExpr(S, Expr, First);
+ diagnoseWellFormedUnsatisfiedConstraintExpr(
+ S, cast<const class Expr *>(Record), First);
}
void Sema::DiagnoseUnsatisfiedConstraint(
const ConstraintSatisfaction &Satisfaction, SourceLocation Loc,
bool First) {
+
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
- std::vector<bool> Prevs;
- for (auto &Record : Satisfaction.Details) {
- if (Record.isNull()) {
- First = Prevs.back();
- Prevs.pop_back();
- continue;
- }
- diagnoseUnsatisfiedConstraintExpr(*this, Record, Loc, First);
- First = false;
- Loc = {};
- if (isa_and_nonnull<ConceptSpecializationExpr>(
- Record.dyn_cast<const Expr *>())) {
- Prevs.push_back(First);
- First = true;
- }
- }
+ ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.Details, Loc, First);
}
void Sema::DiagnoseUnsatisfiedConstraint(
const ConceptSpecializationExpr *ConstraintExpr) {
+
const ASTConstraintSatisfaction &Satisfaction =
ConstraintExpr->getSatisfaction();
- bool First = true;
- SourceLocation Loc = ConstraintExpr->getBeginLoc();
+
assert(!Satisfaction.IsSatisfied &&
"Attempted to diagnose a satisfied constraint");
- std::vector<bool> Prevs;
- for (auto &Record : Satisfaction) {
- if (Record.isNull()) {
- First = Prevs.back();
- Prevs.pop_back();
- continue;
- }
- diagnoseUnsatisfiedConstraintExpr(*this, Record, Loc, First);
- Loc = {};
- if (isa_and_nonnull<ConceptSpecializationExpr>(
- Record.dyn_cast<const Expr *>())) {
- Prevs.push_back(First);
- First = true;
- continue;
- }
- }
+
+ ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(),
+ ConstraintExpr->getBeginLoc());
}
const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
@@ -1673,6 +1644,12 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// [...]
E = E->IgnoreParenImpCasts();
+ llvm::FoldingSetNodeID ID;
+ if (D && DiagRecursiveConstraintEval(S, ID, D, E)) {
+ return nullptr;
+ }
+ SatisfactionStackRAII StackRAII(S, D, ID);
+
// C++2a [temp.param]p4:
// [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
// Fold expression is considered atomic constraints per current wording.
@@ -1723,8 +1700,9 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
S.ArgPackSubstIndex);
} else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
- FE && (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
- FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
+ FE && S.getLangOpts().CPlusPlus26 &&
+ (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
+ FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
// Normalize fold expressions in C++26.
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index 4c5ac5dcbbd04..be3de8c6ad49a 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -94,7 +94,7 @@ static_assert(__is_constructible(Movable, int));
// expected-error at -1{{static assertion failed due to requirement '__is_constructible(Movable, int)'}} \
// expected-note at -1 2{{}}
// expected-error@#err-self-constraint-1{{satisfaction of constraint '__is_constructible(Movable, T)' depends on itself}}
-// expected-note@#err-self-constraint-1 4{{}}
+// expected-note@#err-self-constraint-1 3{{}}
template <typename T>
struct Members {
@@ -191,7 +191,6 @@ void h(short n) { f(n); }
// expected-note at -1{{while checking constraint satisfaction for template}}
// expected-note@#GH62096-note1{{in instantiation}}
// expected-note@#GH62096-note1{{while substituting template arguments into constraint expression here}}
-// expected-note@#GH62096-note2{{while substituting template arguments into constraint expression here}}
// expected-note@#GH62096-note2{{while checking the satisfaction of concept}}
// expected-note@#GH62096-err {{expression evaluates}}
}
More information about the cfe-commits
mailing list