[clang] [Clang][WIP] Normalize constraints before checking for satisfaction (PR #141776)
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 11 08:39:03 PDT 2025
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/141776
>From abea92313b85d47670331c25cb497dde1b82ecf2 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 01/31] [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 | 1177 +++++++++--------
clang/lib/Sema/SemaDeclCXX.cpp | 12 +-
clang/lib/Sema/SemaExprCXX.cpp | 11 +-
clang/lib/Sema/SemaTemplate.cpp | 44 +-
clang/lib/Sema/SemaTemplateDeduction.cpp | 8 +
clang/lib/Sema/SemaTemplateInstantiate.cpp | 230 ++--
clang/lib/Sema/SemaTemplateVariadic.cpp | 2 +
clang/lib/Sema/TreeTransform.h | 35 +-
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, 1274 insertions(+), 806 deletions(-)
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index 72da0059744f2..cc2923c9e4e71 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -28,6 +28,7 @@ namespace clang {
class ConceptDecl;
class TemplateDecl;
+class ConceptReference;
class Expr;
class NamedDecl;
struct PrintingPolicy;
@@ -35,6 +36,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;
@@ -49,7 +51,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;
@@ -80,7 +82,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 69481e85d87fa..046b0338eb1ff 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -475,7 +475,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 {
struct TemplateTemplateArgLocInfo {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1dfc276147fd4..69eed4974bb76 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11673,7 +11673,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,
@@ -12760,6 +12761,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) {
@@ -13322,6 +13326,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.
///
@@ -14660,6 +14669,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
@@ -14684,44 +14697,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
@@ -14787,16 +14767,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
@@ -14831,7 +14811,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 fe907078af275..5d4078e4aa978 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 d658890e076c2..cec092b52a290 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -24,7 +24,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 =
@@ -74,9 +76,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 95dd42681d870..32d0d01fa3461 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5829,6 +5829,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 da85959c005fe..e891d6f66e1be 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);
-
-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();
+ // TODO substitute at the appropriate depth
+ // Template->getTemplateDepth();
- 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 UnsignedOrNone EvaluateFoldExpandedConstraintSize(
+ Sema &S, const FoldExpandedConstraint &FE, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+ ConstraintSatisfaction &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 ExprResult calculateConstraintSatisfaction(
- Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
- const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
+static bool calculateConstraintSatisfaction(
+ Sema &S, const FoldExpandedConstraint &FE, const NamedDecl *Template,
+ SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction) {
- return calculateConstraintSatisfaction(S, ConstraintExpr, Template,
- TemplateNameLoc, MLTAL, 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,57 +701,58 @@ 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())
+ llvm::ArrayRef<TemplateArgument> Args;
+ if (TemplateArgsLists.getNumLevels() != 0)
+ Args = TemplateArgsLists.getInnermost();
+
+ std::optional<Sema::InstantiatingTemplate> SynthesisContext;
+ if (!TopLevelConceptId) {
+ SynthesisContext.emplace(S, TemplateIDRange.getBegin(),
+ Sema::InstantiatingTemplate::ConstraintsCheck{},
+ const_cast<NamedDecl *>(Template), Args,
+ TemplateIDRange);
+ }
+
+ const NormalizedConstraint *C =
+ S.getNormalizedAssociatedConstraints(Template, AssociatedConstraints);
+ if (!C) {
+ Satisfaction.IsSatisfied = false;
return true;
+ }
- for (const AssociatedConstraint &AC : AssociatedConstraints) {
- if (AC.isNull())
- return true;
+ if (TopLevelConceptId)
+ C = ConceptIdConstraint::Create(S.getASTContext(), TopLevelConceptId,
+ const_cast<NormalizedConstraint *>(C),
+ Template, S.ArgPackSubstIndex);
- Sema::ArgPackSubstIndexRAII _(S, AC.ArgPackSubstIndex);
- ExprResult Res = calculateConstraintSatisfaction(
- S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
- AC.ConstraintExpr, Satisfaction);
- if (Res.isInvalid())
- return true;
+ ExprResult Res = calculateConstraintSatisfaction(
+ S, *C, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
+ Satisfaction, S.ArgPackSubstIndex);
+
+ if (Res.isInvalid())
+ return true;
+
+ if (Res.isUsable() && ConvertedExpr)
+ *ConvertedExpr = Res.get();
- 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;
- }
- }
return false;
}
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.
@@ -644,8 +769,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;
@@ -653,10 +782,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;
}
@@ -687,14 +816,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(
@@ -853,50 +986,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) {
@@ -1201,20 +1290,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:
@@ -1248,31 +1324,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
@@ -1333,22 +1441,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())
@@ -1378,101 +1470,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)
@@ -1491,7 +1614,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()
@@ -1504,21 +1629,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(
@@ -1528,64 +1701,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);
@@ -1604,15 +1740,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(),
@@ -1620,7 +1756,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>,
@@ -1631,23 +1767,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.
@@ -1660,28 +1796,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(
@@ -1692,8 +1828,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);
@@ -1785,7 +1923,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;
@@ -1838,24 +1976,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 -----------------------------------
@@ -1898,8 +2018,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 [...]
@@ -1915,10 +2035,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());
@@ -1936,11 +2056,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;
@@ -1984,38 +2104,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) {
@@ -2030,12 +2159,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;
@@ -2085,9 +2214,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 0477d37cac4c5..7800aff5bc2ce 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17870,13 +17870,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.
+ const ASTConstraintSatisfaction &Satisfaction =
+ ConceptIDExpr->getSatisfaction();
+ if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) {
Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
<< !HasMessage << Msg.str() << AssertExpr->getSourceRange();
- ConstraintSatisfaction Satisfaction;
- if (!CheckConstraintSatisfaction(ConceptIDExpr, Satisfaction))
- DiagnoseUnsatisfiedConstraint(Satisfaction);
+ // 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 29c9c47d4504c..062367656c020 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7947,7 +7947,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) {
@@ -7956,8 +7963,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 72d98ca391f4b..0e04fb1238416 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4797,13 +4797,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())
@@ -4829,24 +4827,35 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
*TemplateArgs, CTAI.CanonicalConverted);
MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.CanonicalConverted,
/*Final=*/false);
+ 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) {
+
LocalInstantiationScope Scope(*this);
EnterExpressionEvaluationContext EECtx{
*this, ExpressionEvaluationContext::Unevaluated, CSD};
- if (!AreArgsDependent &&
- CheckConstraintSatisfaction(
+ ContextRAII CurContext(*this, CSD->getDeclContext(),
+ /*NewThisContext=*/false);
+
+ Error = 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));
+ Satisfaction, CL);
+ }
+
+ if (!DoCheckConstraintSatisfaction)
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = Error;
+
return ConceptSpecializationExpr::Create(
Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction);
}
@@ -6092,7 +6101,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 3aa808e165bb6..2ea76cd08862b 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -7205,6 +7205,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 5e8dfd19fd6fa..fc76d8466d7d1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1387,7 +1387,8 @@ getPackSubstitutedTemplateArgument(Sema &S, TemplateArgument Arg) {
// Template Instantiation for Types
//===----------------------------------------------------------------------===/
namespace {
- class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
+
+class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
const MultiLevelTemplateArgumentList &TemplateArgs;
SourceLocation Loc;
DeclarationName Entity;
@@ -1399,13 +1400,14 @@ namespace {
// Whether an incomplete substituion should be treated as an error.
bool BailOutOnIncomplete;
- private:
+ bool PreserveArgumentPacks = false;
+
// CWG2770: Function parameters should be instantiated when they are
// needed by a satisfaction check of an atomic constraint or
// (recursively) by another function parameter.
bool maybeInstantiateFunctionParameterToScope(ParmVarDecl *OldParm);
- public:
+public:
typedef TreeTransform<TemplateInstantiator> inherited;
TemplateInstantiator(Sema &SemaRef,
@@ -1415,12 +1417,16 @@ namespace {
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}
- void setEvaluateConstraints(bool B) {
- EvaluateConstraints = B;
- }
- bool getEvaluateConstraints() {
- return EvaluateConstraints;
- }
+ 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.
@@ -1435,6 +1441,25 @@ namespace {
/// Returns the name of the entity being instantiated, if any.
DeclarationName getBaseEntity() { return Entity; }
+ bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
+ SourceRange PatternRange,
+ ArrayRef<UnexpandedParameterPack> Unexpanded,
+ bool &ShouldExpand, bool &RetainExpansion,
+ UnsignedOrNone &NumExpansions) {
+ if (SemaRef.CurrentInstantiationScope &&
+ SemaRef.inConstraintSubstitution()) {
+ for (UnexpandedParameterPack ParmPack : Unexpanded) {
+ NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
+ if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(VD);
+ PVD && maybeInstantiateFunctionParameterToScope(PVD))
+ return true;
+ }
+ }
+
+ return getSema().CheckParameterPacksForExpansion(
+ EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
+ RetainExpansion, NumExpansions);
+ }
/// Returns whether any substitution so far was incomplete.
bool getIsIncomplete() const { return IsIncomplete; }
@@ -1461,16 +1486,6 @@ namespace {
ArrayRef<UnexpandedParameterPack> Unexpanded,
bool &ShouldExpand, bool &RetainExpansion,
UnsignedOrNone &NumExpansions) {
- if (SemaRef.CurrentInstantiationScope &&
- SemaRef.inConstraintSubstitution()) {
- for (UnexpandedParameterPack ParmPack : Unexpanded) {
- NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>();
- if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(VD);
- PVD && maybeInstantiateFunctionParameterToScope(PVD))
- return true;
- }
- }
-
return getSema().CheckParameterPacksForExpansion(
EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
RetainExpansion, NumExpansions);
@@ -1482,10 +1497,10 @@ namespace {
TemplateArgument ForgetPartiallySubstitutedPack() {
TemplateArgument Result;
- if (NamedDecl *PartialPack
- = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
- MultiLevelTemplateArgumentList &TemplateArgs
- = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+ 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)) {
@@ -1505,16 +1520,20 @@ namespace {
if (Arg.isNull())
return;
- if (NamedDecl *PartialPack
- = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
- MultiLevelTemplateArgumentList &TemplateArgs
- = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+ 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);
}
}
+ bool ShouldPreserveTemplateArgumentsPacks() const {
+ return PreserveArgumentPacks;
+ }
+
TemplateArgument
getTemplateArgumentPackPatternForRewrite(const TemplateArgument &TA) {
if (TA.getKind() != TemplateArgument::Pack)
@@ -1591,8 +1610,7 @@ namespace {
/// as an instantiated local.
VarDecl *RebuildExceptionDecl(VarDecl *ExceptionDecl,
TypeSourceInfo *Declarator,
- SourceLocation StartLoc,
- SourceLocation NameLoc,
+ SourceLocation StartLoc, SourceLocation NameLoc,
IdentifierInfo *Name);
/// Rebuild the Objective-C exception declaration and register the
@@ -1600,8 +1618,15 @@ namespace {
VarDecl *RebuildObjCExceptionDecl(VarDecl *ExceptionDecl,
TypeSourceInfo *TSInfo, QualType T);
- TemplateName
- TransformTemplateName(NestedNameSpecifierLoc &QualifierLoc,
+ /// Check for tag mismatches when instantiating an
+ /// elaborated type.
+ QualType RebuildElaboratedType(SourceLocation KeywordLoc,
+ ElaboratedTypeKeyword Keyword,
+ NestedNameSpecifierLoc QualifierLoc,
+ QualType T);
+
+ TemplateName
+ TransformTemplateName(NestedNameSpecifierLoc &QualifierLoc,
SourceLocation TemplateKWLoc, TemplateName Name,
SourceLocation NameLoc,
QualType ObjectType = QualType(),
@@ -1628,8 +1653,8 @@ namespace {
NonTypeTemplateParmDecl *D);
ExprResult TransformSubstNonTypeTemplateParmPackExpr(
SubstNonTypeTemplateParmPackExpr *E);
- ExprResult TransformSubstNonTypeTemplateParmExpr(
- SubstNonTypeTemplateParmExpr *E);
+ ExprResult
+ TransformSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E);
/// Rebuild a DeclRefExpr for a VarDecl reference.
ExprResult RebuildVarDeclRefExpr(ValueDecl *PD, SourceLocation Loc);
@@ -1714,7 +1739,7 @@ namespace {
return inherited::ComputeSizeOfPackExprWithoutSubstitution(PackArgs);
}
- template<typename Fn>
+ template <typename Fn>
QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
FunctionProtoTypeLoc TL,
CXXRecordDecl *ThisContext,
@@ -1733,10 +1758,12 @@ namespace {
TemplateTypeParmTypeLoc TL,
bool SuppressObjCLifetime);
- QualType BuildSubstTemplateTypeParmType(
- TypeLocBuilder &TLB, bool SuppressObjCLifetime, bool Final,
- Decl *AssociatedDecl, unsigned Index, UnsignedOrNone PackIndex,
- TemplateArgument Arg, SourceLocation NameLoc);
+ 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)
@@ -1870,13 +1897,15 @@ namespace {
return false;
}
- TemplateParameterList *TransformTemplateParameterList(
- TemplateParameterList *OrigTPL) {
- if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
+ TemplateParameterList *
+ TransformTemplateParameterList(TemplateParameterList *OrigTPL) {
+ if (!OrigTPL || !OrigTPL->size())
+ return OrigTPL;
DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
TemplateDeclInstantiator DeclInstantiator(getSema(),
- /* DeclContext *Owner */ Owner, TemplateArgs);
+ /* DeclContext *Owner */ Owner,
+ TemplateArgs);
DeclInstantiator.setEvaluateConstraints(EvaluateConstraints);
return DeclInstantiator.SubstTemplateParams(OrigTPL);
}
@@ -1894,13 +1923,13 @@ namespace {
SmallVectorImpl<ParmVarDecl *> &TransParams,
Sema::ExtParameterInfoBuilder &PInfos);
- private:
+private:
ExprResult
transformNonTypeTemplateParmRef(Decl *AssociatedDecl, const NamedDecl *parm,
SourceLocation loc, TemplateArgument arg,
UnsignedOrNone PackIndex, bool Final);
- };
-}
+};
+} // namespace
bool TemplateInstantiator::AlreadyTransformed(QualType T) {
if (T.isNull())
@@ -2879,69 +2908,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,
+
+ if (Req->isDependent() || AlwaysRebuild()) {
+ Sema::InstantiatingTemplate ReqInst(
+ SemaRef, Constraint->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);
+ 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 "
+
+ 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.");
}
- 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);
- }
- 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,
@@ -4443,6 +4479,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 377f2ed3552d3..7fd4e6b323a3e 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -836,6 +836,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 1863e7f97e3f1..4c792c30e67e5 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
@@ -660,6 +666,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);
@@ -5071,12 +5081,32 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
typedef TemplateArgumentLocInventIterator<Derived,
TemplateArgument::pack_iterator>
PackLocIterator;
+
+ TemplateArgumentListInfo *PackOutput = &Outputs;
+ TemplateArgumentListInfo New;
+
+ if (getDerived().ShouldPreserveTemplateArgumentsPacks()) {
+ PackOutput = &New;
+ }
+
if (TransformTemplateArguments(
PackLocIterator(*this, In.getArgument().pack_begin()),
- PackLocIterator(*this, In.getArgument().pack_end()), Outputs,
+ 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;
}
@@ -5169,7 +5199,6 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
}
return false;
-
}
//===----------------------------------------------------------------------===//
@@ -6140,7 +6169,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 be9bad9e96cc1..bf2902d8931f2 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/Serialization/ASTReader.h"
@@ -474,10 +475,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 5c2948f67d0ee..ced3500938cb0 100644
--- a/clang/test/CXX/drs/cwg25xx.cpp
+++ b/clang/test/CXX/drs/cwg25xx.cpp
@@ -243,15 +243,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
@@ -263,8 +264,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 59e6a48e48878..424fbaabc124e 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{{while substituting deduced template arguments into function template 'f3' [with T = int]}}
//
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 99a82d96d321b..d018368cb96c4 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -127,13 +127,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 6bed1790051f3..aa4ed53ec4ed3 100644
--- a/clang/test/SemaTemplate/concepts-recovery-expr.cpp
+++ b/clang/test/SemaTemplate/concepts-recovery-expr.cpp
@@ -4,7 +4,7 @@
constexpr bool CausesRecoveryExpr = "test" + 1.0f;
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 c746596b8d7f63530ba7044dc44c79b3dbe5eceb 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 02/31] 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 | 168 ++++++++----------
...overload-resolution-deferred-templates.cpp | 1 -
4 files changed, 107 insertions(+), 105 deletions(-)
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index cc2923c9e4e71..9babfd39dd668 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -33,6 +33,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 {
@@ -51,7 +59,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;
@@ -59,7 +66,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);
@@ -77,13 +84,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.
///
@@ -103,6 +103,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,
@@ -284,6 +288,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 cec092b52a290..d533aac707cbc 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -28,6 +28,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);
@@ -119,6 +122,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 e891d6f66e1be..1f598b3df6dbc 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)
+ if (MLTAL) {
+ 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 (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);
}
@@ -1325,55 +1318,54 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
}
}
-static void
-diagnoseUnsatisfiedConceptIdExpr(Sema &S, const ConceptSpecializationExpr *CSE,
+static void diagnoseUnsatisfiedConceptIdExpr(Sema &S,
+ const ConceptReference *Concept,
SourceLocation Loc, bool First) {
- if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
+ 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 {
+ for (auto &Record : Records) {
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 *>())) {
+ diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req);
+ Loc = {};
+ if (isa<const ConceptReference *>(Record)) {
Prevs.push_back(First);
First = true;
continue;
}
First = false;
}
- 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,
@@ -1455,6 +1447,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);
@@ -1470,74 +1466,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 *>()) {
+ 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(
@@ -1732,6 +1703,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.
@@ -1782,7 +1759,8 @@ 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 && 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 135865c8450f5..0e550ff87ef7b 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -200,7 +200,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}}
}
>From 8d0a533331caa9f22b11b9f8f4d6350340183677 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 30 May 2025 15:19:16 +0200
Subject: [PATCH 03/31] Fix some tests
---
.../expr/expr.prim/expr.prim.req/compound-requirement.cpp | 4 ++--
clang/test/SemaCXX/invalid-requirement-requires-expr.cpp | 7 +++----
.../SemaTemplate/instantiate-expanded-type-constraint.cpp | 3 ++-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
index 31587a956b8ab..1253c29e14ffc 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
@@ -89,7 +89,7 @@ template<typename T>
concept Large = sizeof(typename remove_reference<T>::type) >= 4;
// expected-note at -1{{because 'sizeof(typename remove_reference<short &>::type) >= 4' (2 >= 4) evaluated to false}}
-template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large':}}
+template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large'}}
struct r7 {};
using r7i1 = r7<int>;
@@ -160,7 +160,7 @@ namespace std_example {
template<typename T> concept C2 =
requires(T x) {
{*x} -> same_as<typename T::inner>;
- // expected-note at -1{{because type constraint 'same_as<int, typename std_example::T2::inner>' was not satisfied:}}
+ // expected-note at -1{{because 'same_as<int, typename T2::inner>' evaluated to false}}
// expected-note at -2{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}}
};
diff --git a/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp b/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp
index 097ada3caa135..e9a4a1f4828fd 100644
--- a/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp
+++ b/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp
@@ -1,6 +1,6 @@
// RUN: %clang -fsyntax-only -std=c++2a -Xclang -verify -ftemplate-depth=5 -ftemplate-backtrace-limit=4 %s
-// RequiresExpr contains invalid requirement. (Eg. Highly recurisive template).
+// RequiresExpr contains invalid requirement. (Eg. Highly recursive template).
template<int x>
struct A { static constexpr bool far(); };
class B {
@@ -17,10 +17,9 @@ constexpr bool A<x>::far() {
b.data_member;
requires A<x-1>::far(); // #Invalid
// expected-error@#Invalid {{recursive template instantiation exceeded maximum depth}}
- // expected-note@#Invalid {{in instantiation}}
- // expected-note@#Invalid 2 {{while}}
+ // expected-note@#Invalid 2{{in instantiation}}
+ // expected-note@#Invalid 1 {{while}}
// expected-note@#Invalid {{contexts in backtrace}}
- // expected-note@#Invalid {{increase recursive template instantiation depth}}
};
}
static_assert(A<1>::far());
diff --git a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
index 6f25c78346e08..de4a4847d0996 100644
--- a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
+++ b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
@@ -7,7 +7,7 @@ template<typename T>
constexpr bool is_same_v<T, T> = true;
template<typename T, typename U>
-concept same_as = is_same_v<T, U>;
+concept same_as = is_same_v<T, U>; //#is_same_v
template<typename T, typename... Us>
concept either = (is_same_v<T, Us> || ...);
@@ -16,6 +16,7 @@ template<typename... Ts>
struct T {
template<same_as<Ts>... Us>
// expected-note at -1{{because 'same_as<int, bool>' evaluated to false}}
+ // expected-note@#is_same_v{{because 'is_same_v<int, bool>' evaluated to false}}
static void foo(Us... u, int x) { };
// expected-note at -1{{candidate template ignored: deduced too few arguments}}
// expected-note at -2{{candidate template ignored: constraints not satisfied}}
>From b57542125215d4e349e6c70afb06331706ad4ab0 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sat, 31 May 2025 11:05:09 +0200
Subject: [PATCH 04/31] Fold expression fixes
---
clang/lib/Sema/SemaConcept.cpp | 15 +++++++++++++--
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 2 +-
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 1f598b3df6dbc..b66920897ed3a 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -533,15 +533,26 @@ static bool calculateConstraintSatisfaction(
if (!NumExpansions)
return false;
+ if(*NumExpansions == 0) {
+ Satisfaction.IsSatisfied = Conjunction;
+ return true;
+ }
+
+ //bool HasAnyFailed = false;
for (unsigned I = 0; I < *NumExpansions; I++) {
Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
+ Satisfaction.IsSatisfied = false;
+ Satisfaction.ContainsErrors = false;
bool Success = calculateConstraintSatisfaction(
S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
*SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
if (!Success)
return false;
- bool IsRHSSatisfied = Satisfaction.IsSatisfied;
- if (!Conjunction && IsRHSSatisfied) {
+ if(!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors) {
+ if(Conjunction)
+ return true;
+ }
+ if (!Conjunction && Satisfaction.IsSatisfied) {
auto EffectiveDetailEnd = Satisfaction.Details.begin();
std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
Satisfaction.Details.erase(EffectiveDetailEnd,
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 4220486d3aed3..55eeb7f564870 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -std=c++2c -verify %s
-template <class T> concept A = true;
+template <class T> concept A = (T(), true);
template <class T> concept C = A<T> && true;
template <class T> concept D = A<T> && __is_same(T, int);
>From 32058fae30c9f0fc71510c98e69e55f30bf5fa7f Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sun, 1 Jun 2025 12:43:44 +0200
Subject: [PATCH 05/31] fix nested diags and canonicalization
---
clang/include/clang/Sema/Sema.h | 5 +--
clang/lib/Sema/SemaConcept.cpp | 32 ++++++-------------
.../instantiate-abbreviated-template.cpp | 2 ++
3 files changed, 15 insertions(+), 24 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 69eed4974bb76..c706e682c27c9 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14772,8 +14772,9 @@ class Sema final : public SemaBase {
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
- void DiagnoseUnsatisfiedConstraint(
- const ConceptSpecializationExpr *ConstraintExpr);
+ void
+ DiagnoseUnsatisfiedConstraint(const ConceptSpecializationExpr *ConstraintExpr,
+ bool First = true);
const NormalizedConstraint *getNormalizedAssociatedConstraints(
ConstrainedDeclOrNestedRequirement Entity,
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index b66920897ed3a..52a7efd81ffbc 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -380,11 +380,12 @@ SubstitutionInTemplateArguments(
if (I < Used.size() && Used[I]) {
// SubstitutedOuterMost[I].dump();
// SubstArgs[MappedIndex].getArgument().dump();
+ TemplateArgument Arg = S.Context.getCanonicalTemplateArgument(
+ SubstArgs[MappedIndex++].getArgument());
if (I < SubstitutedOuterMost.size())
- SubstitutedOuterMost[I] = SubstArgs[MappedIndex++].getArgument();
+ SubstitutedOuterMost[I] = Arg;
else
- SubstitutedOuterMost.push_back(
- SubstArgs[MappedIndex++].getArgument());
+ SubstitutedOuterMost.push_back(Arg);
}
MLTAL.replaceOutermostTemplateArguments(
const_cast<NamedDecl *>(Constraint.getConstraintDecl()),
@@ -548,10 +549,6 @@ static bool calculateConstraintSatisfaction(
*SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
if (!Success)
return false;
- if(!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors) {
- if(Conjunction)
- return true;
- }
if (!Conjunction && Satisfaction.IsSatisfied) {
auto EffectiveDetailEnd = Satisfaction.Details.begin();
std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
@@ -1353,21 +1350,14 @@ static void diagnoseUnsatisfiedConstraintExpr(
static void DiagnoseUnsatisfiedConstraint(
Sema &S, ArrayRef<UnsatisfiedConstraintRecord> Records, SourceLocation Loc,
bool First = true, concepts::NestedRequirement *Req = nullptr) {
- std::vector<bool> Prevs;
for (auto &Record : Records) {
if (Record.isNull()) {
- First = Prevs.back();
- Prevs.pop_back();
+ First = false;
continue;
}
diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req);
Loc = {};
- if (isa<const ConceptReference *>(Record)) {
- Prevs.push_back(First);
- First = true;
- continue;
- }
- First = false;
+ First = isa<const ConceptReference *>(Record);
}
}
@@ -1460,7 +1450,7 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
return;
} else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
// Drill down concept ids treated as atomic constraints
- S.DiagnoseUnsatisfiedConstraint(CSE);
+ S.DiagnoseUnsatisfiedConstraint(CSE, First);
return;
} else if (auto *TTE = dyn_cast<TypeTraitExpr>(SubstExpr);
TTE && TTE->getTrait() == clang::TypeTrait::BTT_IsDeducible) {
@@ -1510,7 +1500,7 @@ void Sema::DiagnoseUnsatisfiedConstraint(
}
void Sema::DiagnoseUnsatisfiedConstraint(
- const ConceptSpecializationExpr *ConstraintExpr) {
+ const ConceptSpecializationExpr *ConstraintExpr, bool First) {
const ASTConstraintSatisfaction &Satisfaction =
ConstraintExpr->getSatisfaction();
@@ -1519,7 +1509,7 @@ void Sema::DiagnoseUnsatisfiedConstraint(
"Attempted to diagnose a satisfied constraint");
::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(),
- ConstraintExpr->getBeginLoc());
+ ConstraintExpr->getBeginLoc(), First);
}
const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
@@ -1618,11 +1608,9 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
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);
+ llvm::copy(SubstArgs.arguments(), TempArgs);
N.updateParameterMapping(
N.mappingOccurenceList(),
MutableArrayRef<TemplateArgumentLoc>(TempArgs, SubstArgs.size()));
diff --git a/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp b/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp
index 1f2171a25ebb0..9bfd472de8486 100644
--- a/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp
+++ b/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
+// RUN: %clang_cc1 -std=c++2c -x c++ %s -verify
+
template<typename...>
concept C = false; // expected-note 9{{because}}
>From 7a24d57da1b5b91f13c181488592732e703c1cd3 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sun, 1 Jun 2025 13:30:32 +0200
Subject: [PATCH 06/31] fix compouund constraint checking
---
clang/lib/Sema/SemaConcept.cpp | 21 +++++++++++++------
.../CXX/expr/expr.prim/expr.prim.id/p3.cpp | 1 -
clang/test/SemaTemplate/concepts.cpp | 7 ++++---
3 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 52a7efd81ffbc..6a5393084f952 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -550,9 +550,7 @@ static bool calculateConstraintSatisfaction(
if (!Success)
return false;
if (!Conjunction && Satisfaction.IsSatisfied) {
- auto EffectiveDetailEnd = Satisfaction.Details.begin();
- std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
- Satisfaction.Details.erase(EffectiveDetailEnd,
+ Satisfaction.Details.erase(Satisfaction.Details.begin() + EffectiveDetailEndIndex,
Satisfaction.Details.end());
break;
}
@@ -635,24 +633,34 @@ static bool calculateConstraintSatisfaction(
ConstraintSatisfaction &Satisfaction,
UnsignedOrNone PackSubstitutionIndex) {
+ auto EffectiveDetailEndIndex = Satisfaction.Details.size();
+
bool Ok = calculateConstraintSatisfaction(
S, Constraint.getLHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
if (!Ok || Satisfaction.ContainsErrors)
- return false;
+ return Ok;
if (Satisfaction.IsSatisfied &&
Constraint.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) {
return true;
}
if (!Satisfaction.IsSatisfied &&
- Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction)
+ Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction) {
return true;
+ }
- return calculateConstraintSatisfaction(S, Constraint.getRHS(), Template,
+ Satisfaction.ContainsErrors = false;
+ Satisfaction.IsSatisfied = false;
+
+ Ok = calculateConstraintSatisfaction(S, Constraint.getRHS(), Template,
TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
+ if(Ok && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
+ Satisfaction.Details.erase(Satisfaction.Details.begin() + EffectiveDetailEndIndex,
+ Satisfaction.Details.end());
+ return Ok;
}
static bool calculateConstraintSatisfaction(
@@ -1365,6 +1373,7 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::NestedRequirement *Req,
bool First) {
DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(),
+ Req->hasInvalidConstraint() ? SourceLocation() :
Req->getConstraintExpr()->getExprLoc(), First,
Req);
}
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 f4a54535524f2..af2fc938fbea2 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
@@ -141,7 +141,6 @@ concept C7 = sizeof(T) == 1 || sizeof(
static_assert(!C6<short>);
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/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index d63ad01b35800..7148790f87ef9 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -996,13 +996,13 @@ template<class>
concept True = true;
template<class>
-concept False = false; // expected-note 9 {{'false' evaluated to false}}
+concept False = false; // expected-note 8 {{'false' evaluated to false}}
template<class>
concept Irrelevant = false;
template <typename T>
-concept ErrorRequires = requires(ErrorRequires auto x) { x; };
+concept ErrorRequires = requires(ErrorRequires auto x) { x; }; //#GH54678-ill-formed-concept
// expected-error at -1 {{a concept definition cannot refer to itself}} \
// expected-error at -1 {{'auto' not allowed in requires expression parameter}} \
// expected-note at -1 {{declared here}}
@@ -1023,7 +1023,8 @@ template<class T> void eee(T t) // expected-note {{candidate template ignored: c
requires (Irrelevant<T> || Irrelevant<T> || True<T>) && False<T> {} // expected-note {{'long' does not satisfy 'False'}}
template<class T> void fff(T t) // expected-note {{candidate template ignored: constraints not satisfied}}
-requires((ErrorRequires<T> || False<T> || True<T>) && False<T>) {} // expected-note {{'unsigned long' does not satisfy 'False'}}
+requires((ErrorRequires<T> || False<T> || True<T>) && False<T>) {} // expected-note {{because 'unsigned long' does not satisfy 'ErrorRequires'}}
+// // expected-note@#GH54678-ill-formed-concept {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
void test() {
aaa(42); // expected-error {{no matching function}}
>From e61458d5da0d9f036cd289393d0dc045ac1590c5 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 2 Jun 2025 15:31:16 +0200
Subject: [PATCH 07/31] fix rebase
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index fc76d8466d7d1..c509ca6bb13f4 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1481,16 +1481,6 @@ class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
return Pack.pack_size() - 1 - *Index;
}
- 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);
- }
-
void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Pack);
}
>From e31d7cceea8b137d42392ee6bf0ebadc62d0b71c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 5 Jun 2025 11:29:05 +0200
Subject: [PATCH 08/31] cleanup
---
clang/lib/Sema/SemaConcept.cpp | 42 ++++++++++++++--------------------
1 file changed, 17 insertions(+), 25 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 6a5393084f952..96dde28529540 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -353,8 +353,6 @@ SubstitutionInTemplateArguments(
const NamedDecl *Template, MultiLevelTemplateArgumentList MLTAL,
llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) {
- Sema::SFINAETrap Trap(S);
-
Sema::InstantiatingTemplate Inst(
S, Constraint.getBeginLoc(),
Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
@@ -363,6 +361,8 @@ SubstitutionInTemplateArguments(
if (Inst.isInvalid())
return std::nullopt;
+ Sema::SFINAETrap Trap(S);
+
// TODO substitute at the appropriate depth
// Template->getTemplateDepth();
@@ -539,7 +539,6 @@ static bool calculateConstraintSatisfaction(
return true;
}
- //bool HasAnyFailed = false;
for (unsigned I = 0; I < *NumExpansions; I++) {
Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
Satisfaction.IsSatisfied = false;
@@ -591,18 +590,19 @@ static bool calculateConstraintSatisfaction(
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);
+ unsigned Depth = Template && Template->getTemplateDepth()
+ ? Template->getTemplateDepth() - 1
+ : 0;
+ AdjustConstraintDepth Adjust(S, Depth);
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))
+
+ if (S.SubstTemplateArguments(TransArgs.arguments(), MLTAL, OutArgs) ||
+ Trap.hasErrorOccurred())
return Ok;
- }
CXXScopeSpec SS;
SS.Adopt(Constraint.getConceptId()->getNestedNameSpecifierLoc());
@@ -1579,22 +1579,14 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
}
TemplateArgumentLoc *TempArgs =
new (S.Context) TemplateArgumentLoc[OccurringIndices.count()];
- for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
- if (OccurringIndices[I])
- new (&(TempArgs)[J++])
- TemplateArgumentLoc(S.getIdentityTemplateArgumentLoc(
- TemplateParams->begin()[I],
- // Here we assume we do not support things like
- // template<typename A, typename B>
- // concept C = ...;
- //
- // template<typename... Ts> requires C<Ts...>
- // struct S { };
- // The above currently yields a diagnostic.
- // We still might have default arguments for concept parameters.
- ArgsAsWritten->NumTemplateArgs > I
+ for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) {
+ SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I
? ArgsAsWritten->arguments()[I].getLocation()
- : SourceLocation()));
+ : SourceLocation();
+ if (OccurringIndices[I])
+ new (&(TempArgs)[J++]) TemplateArgumentLoc(
+ S.getIdentityTemplateArgumentLoc(TemplateParams->begin()[I], Loc));
+ }
N.updateParameterMapping(OccurringIndices,
MutableArrayRef<TemplateArgumentLoc>{
TempArgs, OccurringIndices.count()});
>From 824f7a5f784cc6cc0da0ebadbf75cc28624ccc4c Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 9 Jun 2025 13:11:26 +0800
Subject: [PATCH 09/31] Fix the access checking after normalization
---
clang/lib/Sema/SemaConcept.cpp | 4 ++++
clang/lib/Sema/SemaTemplate.cpp | 3 ---
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 96dde28529540..fa11a00274f7e 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -563,6 +563,10 @@ static bool calculateConstraintSatisfaction(
ConstraintSatisfaction &Satisfaction,
UnsignedOrNone PackSubstitutionIndex) {
+ Sema::ContextRAII CurContext(
+ S, Constraint.getConceptId()->getNamedConcept()->getDeclContext(),
+ /*NewThisContext=*/false);
+
llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 0e04fb1238416..7ae31441b293a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4841,9 +4841,6 @@ ExprResult Sema::CheckConceptTemplateId(
EnterExpressionEvaluationContext EECtx{
*this, ExpressionEvaluationContext::Unevaluated, CSD};
- ContextRAII CurContext(*this, CSD->getDeclContext(),
- /*NewThisContext=*/false);
-
Error = CheckConstraintSatisfaction(
NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
MLTAL,
>From 6f23cbdbfce14b235539e7ad783beb9f8c0df735 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 11 Jun 2025 18:29:44 +0800
Subject: [PATCH 10/31] Fix SemaTemplate/concepts-lambda.cpp
---
clang/include/clang/Sema/Sema.h | 8 ++++++++
clang/include/clang/Sema/Template.h | 1 +
clang/lib/Sema/SemaConcept.cpp | 4 +++-
clang/lib/Sema/SemaTemplate.cpp | 6 ++++--
clang/lib/Sema/SemaTemplateInstantiate.cpp | 19 ++++++++++++++++---
5 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index c706e682c27c9..c8b86604ac183 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -13061,6 +13061,8 @@ class Sema final : public SemaBase {
/// Whether we're substituting into constraints.
bool InConstraintSubstitution;
+ bool InParameterMappingSubstitution;
+
/// The point of instantiation or synthesis within the source code.
SourceLocation PointOfInstantiation;
@@ -13789,11 +13791,17 @@ class Sema final : public SemaBase {
}
/// Determine whether we are currently performing constraint substitution.
+ // FIXME: Rename it
bool inConstraintSubstitution() const {
return !CodeSynthesisContexts.empty() &&
CodeSynthesisContexts.back().InConstraintSubstitution;
}
+ bool inParameterMappingSubstitution() const {
+ return !CodeSynthesisContexts.empty() &&
+ CodeSynthesisContexts.back().InParameterMappingSubstitution;
+ }
+
using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>;
/// \brief create a Requirement::SubstitutionDiagnostic with only a
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 5d4078e4aa978..8ccb478e9f847 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -261,6 +261,7 @@ enum class TemplateSubstitutionKind : char {
// ==
// AssociatedDecl) &&
// "Trying to change incorrect declaration?");
+ TemplateArgumentLists.back().AssociatedDeclAndFinal.setPointer(AssociatedDecl);
TemplateArgumentLists.back().Args = Args;
}
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index fa11a00274f7e..9b56d6994f897 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1675,7 +1675,9 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
-
+ // Don't build Subst* nodes to model lambda expressions.
+ // The transform of Subst* is oblivious to the lambda type.
+ MLTAL.setKind(TemplateSubstitutionKind::Rewrite);
return substituteParameterMappings(S, N, MLTAL,
CSE->getTemplateArgsAsWritten());
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 7ae31441b293a..54c9a39168617 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1181,8 +1181,10 @@ static ExprResult formImmediatelyDeclaredConstraint(
if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) {
ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
- /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD,
- &ConstraintArgs);
+ /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, NamedConcept,
+ &ConstraintArgs,
+ /*DoCheckConstraintSatisfaction=*/
+ !S.inParameterMappingSubstitution());
}
// We have a template template parameter
else {
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index c509ca6bb13f4..48e2cc091ce5e 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -628,10 +628,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
Inst.DeductionInfo = DeductionInfo;
Inst.InstantiationRange = InstantiationRange;
Inst.InConstraintSubstitution =
- Inst.Kind == CodeSynthesisContext::ConstraintSubstitution;
- if (!SemaRef.CodeSynthesisContexts.empty())
+ Inst.Kind == CodeSynthesisContext::ConstraintsCheck;
+ Inst.InParameterMappingSubstitution =
+ Inst.Kind == CodeSynthesisContext::ParameterMappingSubstitution;
+ if (!SemaRef.CodeSynthesisContexts.empty()) {
Inst.InConstraintSubstitution |=
SemaRef.CodeSynthesisContexts.back().InConstraintSubstitution;
+ Inst.InParameterMappingSubstitution |=
+ SemaRef.CodeSynthesisContexts.back().InParameterMappingSubstitution;
+ }
SemaRef.pushCodeSynthesisContext(Inst);
@@ -2204,6 +2209,14 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
// We're rewriting the template parameter as a reference to another
// template parameter.
Arg = getTemplateArgumentPackPatternForRewrite(Arg);
+ if (Arg.getKind() != TemplateArgument::Expression) {
+ assert(SemaRef.inParameterMappingSubstitution());
+ // FIXME: SourceLocation()?
+ ExprResult E = SemaRef.BuildExpressionFromNonTypeTemplateArgument(Arg, SourceLocation());
+ if (E.isInvalid())
+ return E;
+ Arg = TemplateArgument(E.get(), /*IsCanonical=*/false);
+ }
assert(Arg.getKind() == TemplateArgument::Expression &&
"unexpected nontype template argument kind in template rewrite");
// FIXME: This can lead to the same subexpression appearing multiple times
@@ -3224,7 +3237,7 @@ bool Sema::SubstTypeConstraint(
const ASTTemplateArgumentListInfo *TemplArgInfo =
TC->getTemplateArgsAsWritten();
- if (!EvaluateConstraints) {
+ if (!EvaluateConstraints && !inParameterMappingSubstitution()) {
UnsignedOrNone Index = TC->getArgPackSubstIndex();
if (!Index)
Index = SemaRef.ArgPackSubstIndex;
>From 1d57c391ea4914984ad985545e6e5c42f74969c0 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 12 Jun 2025 20:36:34 +0800
Subject: [PATCH 11/31] Fix pack expansion
clang/test/SemaTemplate/alias-template-with-lambdas.cpp
---
clang/include/clang/Sema/SemaConcept.h | 9 ++++---
clang/lib/Sema/SemaConcept.cpp | 34 +++++++++++++++-----------
2 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index 0a79a6b5cc43f..80d557d3827fd 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -65,7 +65,7 @@ struct NormalizedConstraint {
struct AtomicBits {
LLVM_PREFERRED_TYPE(ConstraintKind)
unsigned Kind : 5;
- unsigned : 1;
+ unsigned Placeholder : 1;
unsigned PackSubstitutionIndex : 26;
llvm::SmallBitVector Indexes;
TemplateArgumentLoc *Args;
@@ -114,6 +114,7 @@ struct NormalizedConstraint {
const NamedDecl *ConstraintDecl,
UnsignedOrNone PackIndex)
: Atomic{llvm::to_underlying(ConstraintKind::Atomic),
+ /*Placeholder=*/0,
PackIndex.toInternalRepresentation(),
/*Indexes=*/{},
/*Args=*/nullptr,
@@ -134,7 +135,8 @@ struct NormalizedConstraint {
NormalizedConstraint *SubConstraint,
UnsignedOrNone PackIndex)
: ConceptId{{llvm::to_underlying(ConstraintKind::ConceptId),
- PackIndex.toInternalRepresentation(), /*Indexes=*/{},
+ /*Placeholder=*/0, PackIndex.toInternalRepresentation(),
+ /*Indexes=*/{},
/*Args=*/nullptr, ConceptId, ConstraintDecl},
SubConstraint} {}
@@ -237,7 +239,8 @@ struct NormalizedConstraint {
fromAssociatedConstraints(Sema &S, const NamedDecl *D,
ArrayRef<AssociatedConstraint> ACs);
static NormalizedConstraint *fromConstraintExpr(Sema &S, const NamedDecl *D,
- const Expr *E);
+ const Expr *E,
+ UnsignedOrNone SubstIndex);
};
class CompoundConstraint : public NormalizedConstraint {
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 9b56d6994f897..52db46320b608 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -368,6 +368,8 @@ SubstitutionInTemplateArguments(
TemplateArgumentListInfo SubstArgs;
if (Constraint.hasParameterMapping()) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(
+ S, Constraint.getPackSubstitutionIndex());
if (S.SubstTemplateArgumentsInParameterMapping(
Constraint.getParameterMapping(), MLTAL, SubstArgs) ||
Trap.hasErrorOccurred())
@@ -590,6 +592,8 @@ static bool calculateConstraintSatisfaction(
return Ok;
Sema::SFINAETrap Trap(S);
+ Sema::ArgPackSubstIndexRAII SubstIndex(
+ S, Constraint.getPackSubstitutionIndex());
const ASTTemplateArgumentListInfo *Ori =
Constraint.getConceptId()->getTemplateArgsAsWritten();
@@ -604,7 +608,8 @@ static bool calculateConstraintSatisfaction(
Ori->NumTemplateArgs, TransArgs))
return Ok;
- if (S.SubstTemplateArguments(TransArgs.arguments(), MLTAL, OutArgs) ||
+ if (S.SubstTemplateArguments(TransArgs.arguments(), *SubstitutedArgs,
+ OutArgs) ||
Trap.hasErrorOccurred())
return Ok;
@@ -1685,11 +1690,13 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints(
Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) {
assert(ACs.size() != 0);
- auto Conjunction = fromConstraintExpr(S, D, ACs[0].ConstraintExpr);
+ auto *Conjunction =
+ fromConstraintExpr(S, D, ACs[0].ConstraintExpr, ACs[0].ArgPackSubstIndex);
if (!Conjunction)
return nullptr;
for (unsigned I = 1; I < ACs.size(); ++I) {
- auto Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr);
+ auto *Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr,
+ ACs[I].ArgPackSubstIndex);
if (!Next)
return nullptr;
Conjunction = CompoundConstraint::CreateConjunction(S.getASTContext(),
@@ -1698,9 +1705,8 @@ NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints(
return Conjunction;
}
-NormalizedConstraint *
-NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
- const Expr *E) {
+NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
+ Sema &S, const NamedDecl *D, const Expr *E, UnsignedOrNone SubstIndex) {
assert(E != nullptr);
// C++ [temp.constr.normal]p1.1
@@ -1721,10 +1727,10 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
// See http://cplusplus.github.io/concepts-ts/ts-active.html#28
if (LogicalBinOp BO = E) {
- auto LHS = fromConstraintExpr(S, D, BO.getLHS());
+ auto *LHS = fromConstraintExpr(S, D, BO.getLHS(), SubstIndex);
if (!LHS)
return nullptr;
- auto RHS = fromConstraintExpr(S, D, BO.getRHS());
+ auto *RHS = fromConstraintExpr(S, D, BO.getRHS(), SubstIndex);
if (!RHS)
return nullptr;
@@ -1752,7 +1758,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
ConceptDecl *CD = CSE->getNamedConcept();
SubNF = NormalizedConstraint::fromAssociatedConstraints(
S, CSE->getNamedConcept(),
- AssociatedConstraint(CD->getConstraintExpr()));
+ AssociatedConstraint(CD->getConstraintExpr(), SubstIndex));
if (!SubNF)
return nullptr;
@@ -1762,7 +1768,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
return ConceptIdConstraint::Create(S.getASTContext(),
CSE->getConceptReference(), SubNF, D,
- S.ArgPackSubstIndex);
+ SubstIndex);
} else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
FE && S.getLangOpts().CPlusPlus26 &&
@@ -1777,8 +1783,8 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
: FoldExpandedConstraint::FoldOperatorKind::Or;
if (FE->getInit()) {
- auto LHS = fromConstraintExpr(S, D, FE->getLHS());
- auto RHS = fromConstraintExpr(S, D, FE->getRHS());
+ auto *LHS = fromConstraintExpr(S, D, FE->getLHS(), SubstIndex);
+ auto *RHS = fromConstraintExpr(S, D, FE->getRHS(), SubstIndex);
if (!LHS || !RHS)
return nullptr;
@@ -1795,13 +1801,13 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
: CCK_Disjunction),
RHS);
}
- auto Sub = fromConstraintExpr(S, D, FE->getPattern());
+ auto *Sub = fromConstraintExpr(S, D, FE->getPattern(), SubstIndex);
if (!Sub)
return nullptr;
return FoldExpandedConstraint::Create(S.getASTContext(), FE->getPattern(),
Kind, Sub);
}
- return AtomicConstraint::Create(S.getASTContext(), E, D, S.ArgPackSubstIndex);
+ return AtomicConstraint::Create(S.getASTContext(), E, D, SubstIndex);
}
bool FoldExpandedConstraint::AreCompatibleForSubsumption(
>From 78214690af1c13772d1c8c8a94735b47fe4ea6d5 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 12 Jun 2025 15:34:37 +0200
Subject: [PATCH 12/31] Fix alias-template-with-lambdas.cpp
---
clang/include/clang/Sema/Template.h | 3 ++-
clang/lib/Sema/SemaConcept.cpp | 9 +++++----
clang/lib/Sema/SemaTemplateInstantiate.cpp | 3 ++-
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 8ccb478e9f847..5160fc0b820f5 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -261,7 +261,8 @@ enum class TemplateSubstitutionKind : char {
// ==
// AssociatedDecl) &&
// "Trying to change incorrect declaration?");
- TemplateArgumentLists.back().AssociatedDeclAndFinal.setPointer(AssociatedDecl);
+ TemplateArgumentLists.back().AssociatedDeclAndFinal.setPointer(
+ AssociatedDecl);
TemplateArgumentLists.back().Args = Args;
}
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 52db46320b608..6f38e70cd0cc3 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -593,7 +593,9 @@ static bool calculateConstraintSatisfaction(
Sema::SFINAETrap Trap(S);
Sema::ArgPackSubstIndexRAII SubstIndex(
- S, Constraint.getPackSubstitutionIndex());
+ S, Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex);
const ASTTemplateArgumentListInfo *Ori =
Constraint.getConceptId()->getTemplateArgsAsWritten();
@@ -1766,9 +1768,8 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
if (substituteParameterMappings(S, *SubNF, CSE))
return nullptr;
- return ConceptIdConstraint::Create(S.getASTContext(),
- CSE->getConceptReference(), SubNF, D,
- SubstIndex);
+ return ConceptIdConstraint::Create(
+ S.getASTContext(), CSE->getConceptReference(), SubNF, D, SubstIndex);
} else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
FE && S.getLangOpts().CPlusPlus26 &&
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 48e2cc091ce5e..f56194b81d987 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2212,7 +2212,8 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
if (Arg.getKind() != TemplateArgument::Expression) {
assert(SemaRef.inParameterMappingSubstitution());
// FIXME: SourceLocation()?
- ExprResult E = SemaRef.BuildExpressionFromNonTypeTemplateArgument(Arg, SourceLocation());
+ ExprResult E = SemaRef.BuildExpressionFromNonTypeTemplateArgument(
+ Arg, SourceLocation());
if (E.isInvalid())
return E;
Arg = TemplateArgument(E.get(), /*IsCanonical=*/false);
>From 1edea42287a92d9785f56636e40f2564dbf05691 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 13 Jun 2025 16:06:28 +0800
Subject: [PATCH 13/31] Stash recent changes
---
clang/include/clang/Sema/SemaConcept.h | 3 +-
clang/lib/Sema/SemaConcept.cpp | 45 ++++++++++++++++---------
clang/lib/Sema/SemaTemplate.cpp | 7 ++--
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 1 +
4 files changed, 37 insertions(+), 19 deletions(-)
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index 80d557d3827fd..2f5be9d0b0325 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -78,7 +78,7 @@ struct NormalizedConstraint {
unsigned Kind : 5;
LLVM_PREFERRED_TYPE(FoldOperatorKind)
unsigned FoldOperator : 1;
- unsigned : 26;
+ unsigned Placeholder : 26;
OccurenceList Indexes;
TemplateArgumentLoc *Args;
const Expr *Pattern;
@@ -125,6 +125,7 @@ struct NormalizedConstraint {
NormalizedConstraint *Constraint)
: FoldExpanded{llvm::to_underlying(ConstraintKind::FoldExpanded),
llvm::to_underlying(OpKind),
+ /*Placeholder=*/0,
/*Indexes=*/{},
/*Args=*/nullptr,
Pattern,
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 6f38e70cd0cc3..b4cb167fcc622 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -351,7 +351,11 @@ static std::optional<MultiLevelTemplateArgumentList>
SubstitutionInTemplateArguments(
Sema &S, const NormalizedConstraintWithParamMapping &Constraint,
const NamedDecl *Template, MultiLevelTemplateArgumentList MLTAL,
- llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) {
+ llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost,
+ // FIXME: Having both PackSubstitutionIndex and
+ // NormalizedConstraintWithParamMapping::getPackSubstitutionIndex is
+ // confusing
+ UnsignedOrNone PackSubstitutionIndex) {
Sema::InstantiatingTemplate Inst(
S, Constraint.getBeginLoc(),
@@ -369,7 +373,9 @@ SubstitutionInTemplateArguments(
TemplateArgumentListInfo SubstArgs;
if (Constraint.hasParameterMapping()) {
Sema::ArgPackSubstIndexRAII SubstIndex(
- S, Constraint.getPackSubstitutionIndex());
+ S, Constraint.getPackSubstitutionIndex()
+ ? Constraint.getPackSubstitutionIndex()
+ : PackSubstitutionIndex);
if (S.SubstTemplateArgumentsInParameterMapping(
Constraint.getParameterMapping(), MLTAL, SubstArgs) ||
Trap.hasErrorOccurred())
@@ -405,11 +411,12 @@ static bool calculateConstraintSatisfaction(
llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
- SubstitutedOuterMost);
+ SubstitutedOuterMost,
+ PackSubstitutionIndex);
if (!SubstitutedArgs)
return false;
- Sema::ArgPackSubstIndexRAII(S, PackSubstitutionIndex);
+ Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex);
ExprResult SubstitutedAtomicExpr =
EvaluateAtomicConstraint(S, Constraint.getConstraintExpr(), Template,
TemplateNameLoc, *SubstitutedArgs, Satisfaction);
@@ -526,7 +533,8 @@ static bool calculateConstraintSatisfaction(
S,
static_cast<const NormalizedConstraintWithParamMapping &>(
FE.getNormalizedPattern()),
- Template, MLTAL, SubstitutedOuterMost);
+ // FIXME: Is PackSubstitutionIndex correct?
+ Template, MLTAL, SubstitutedOuterMost, S.ArgPackSubstIndex);
if (!SubstitutedArgs)
return false;
@@ -548,7 +556,9 @@ static bool calculateConstraintSatisfaction(
bool Success = calculateConstraintSatisfaction(
S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
*SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
- if (!Success)
+ // SFINAE errors shouldn't prevent disjunction from evaluating
+ // FIXME: Does !Success == SFINAE errors occurred?
+ if (!Success && Conjunction)
return false;
if (!Conjunction && Satisfaction.IsSatisfied) {
Satisfaction.Details.erase(Satisfaction.Details.begin() + EffectiveDetailEndIndex,
@@ -556,6 +566,10 @@ static bool calculateConstraintSatisfaction(
break;
}
}
+ // Satisfaction.IsSatisfied might be overwritten.
+ // How to handle errors here ?? Shall we substitute into the concept?
+ if (Satisfaction.Details.size() != EffectiveDetailEndIndex)
+ Satisfaction.IsSatisfied = false;
return true;
}
@@ -569,11 +583,6 @@ static bool calculateConstraintSatisfaction(
S, Constraint.getConceptId()->getNamedConcept()->getDeclContext(),
/*NewThisContext=*/false);
- llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
- std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
- SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
- SubstitutedOuterMost);
-
Sema::InstantiatingTemplate Tpl(
S, Constraint.getConceptId()->getBeginLoc(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
@@ -588,6 +597,12 @@ static bool calculateConstraintSatisfaction(
if (Size != Satisfaction.Details.size()) {
+ llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+ std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+ SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
+ SubstitutedOuterMost,
+ PackSubstitutionIndex);
+
if (!SubstitutedArgs)
return Ok;
@@ -633,7 +648,7 @@ static bool calculateConstraintSatisfaction(
SubstitutedConceptId.getAs<ConceptSpecializationExpr>()
->getConceptReference()));
- Satisfaction.Details.push_back(nullptr);
+ // Satisfaction.Details.push_back(nullptr);
}
return Ok;
}
@@ -1790,11 +1805,11 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
return nullptr;
if (FE->isRightFold())
- RHS = FoldExpandedConstraint::Create(S.getASTContext(),
- FE->getPattern(), Kind, RHS);
- else
LHS = FoldExpandedConstraint::Create(S.getASTContext(),
FE->getPattern(), Kind, LHS);
+ else
+ RHS = FoldExpandedConstraint::Create(S.getASTContext(),
+ FE->getPattern(), Kind, RHS);
return CompoundConstraint::Create(
S.getASTContext(), LHS,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 54c9a39168617..dcac1e0b975b8 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5172,10 +5172,11 @@ bool Sema::CheckTemplateTypeArgument(
}
default: {
// We allow instantiating a template with template argument packs when
- // building deduction guides.
+ // building deduction guides or mapping constraint template parameters.
if (Arg.getKind() == TemplateArgument::Pack &&
- CodeSynthesisContexts.back().Kind ==
- Sema::CodeSynthesisContext::BuildingDeductionGuides) {
+ (CodeSynthesisContexts.back().Kind ==
+ Sema::CodeSynthesisContext::BuildingDeductionGuides ||
+ inParameterMappingSubstitution())) {
SugaredConverted.push_back(Arg);
CanonicalConverted.push_back(Arg);
return false;
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 55eeb7f564870..fb2fc95949dab 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -158,6 +158,7 @@ static_assert(And1<int, S>() == 1); // expected-error {{no matching function for
static_assert(And2<S>() == 2);
static_assert(And2<S, S>() == 2);
+// FIXME: Should it compile??
static_assert(And2<int>() == 2);
static_assert(And2<int, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
>From 17b4bbea29bc6a98246fb53a2c8e3c3e47707f0b Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 13 Jun 2025 17:11:42 +0200
Subject: [PATCH 14/31] cleanup
---
clang/lib/Sema/SemaConcept.cpp | 6 ------
1 file changed, 6 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index b4cb167fcc622..a64b56ee5d701 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -647,8 +647,6 @@ static bool calculateConstraintSatisfaction(
UnsatisfiedConstraintRecord(
SubstitutedConceptId.getAs<ConceptSpecializationExpr>()
->getConceptReference()));
-
- // Satisfaction.Details.push_back(nullptr);
}
return Ok;
}
@@ -1385,10 +1383,6 @@ static void DiagnoseUnsatisfiedConstraint(
Sema &S, ArrayRef<UnsatisfiedConstraintRecord> Records, SourceLocation Loc,
bool First = true, concepts::NestedRequirement *Req = nullptr) {
for (auto &Record : Records) {
- if (Record.isNull()) {
- First = false;
- continue;
- }
diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req);
Loc = {};
First = isa<const ConceptReference *>(Record);
>From 2adf94acbe6d0a4693bb0edda8a3e86cfcaf329e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 14 Jun 2025 18:49:03 +0800
Subject: [PATCH 15/31] checkpoint
AST/ByteCode/libcxx/deref-to-array.cpp now compiles
But there are more tests getting regressed now, as I
have disabled ShouldPreserveTemplateArgumentsPacks,
which caused bugs for deref-to-array.cpp
Failed Tests (13):
Clang :: AST/ByteCode/libcxx/minmax.cpp
Clang :: AST/ByteCode/libcxx/primitive-temporary.cpp
Clang :: CXX/drs/cwg25xx.cpp
Clang :: CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
Clang :: CXX/temp/temp.constr/temp.constr.normal/p1.cpp
Clang :: CXX/temp/temp.param/p10-2a.cpp
Clang :: CodeGenCXX/mangle-concept.cpp
Clang :: Modules/pr62943.cppm
Clang :: SemaCXX/cxx2c-fold-exprs.cpp
Clang :: SemaTemplate/concepts-recursive-inst.cpp
Clang :: SemaTemplate/concepts.cpp
Clang :: SemaTemplate/instantiate-template-argument.cpp
Clang :: SemaTemplate/temp_arg_nontype_cxx2c.cpp
---
clang/lib/Sema/SemaConcept.cpp | 33 +++++++++++++++++-----
clang/lib/Sema/SemaTemplate.cpp | 11 ++++++++
clang/lib/Sema/SemaTemplateDeduction.cpp | 11 +++++---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 8 ++++--
clang/lib/Sema/TreeTransform.h | 6 ++--
5 files changed, 52 insertions(+), 17 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index a64b56ee5d701..ba83408dff36d 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -380,15 +380,23 @@ SubstitutionInTemplateArguments(
Constraint.getParameterMapping(), MLTAL, SubstArgs) ||
Trap.hasErrorOccurred())
return std::nullopt;
+ // Sema::CheckTemplateArgumentInfo CTAI;
+ // auto *TD = const_cast<TemplateDecl *>(
+ // cast<TemplateDecl>(Constraint.getConstraintDecl()));
+ // if (S.CheckTemplateArgumentList(TD, TD->getLocation(), SubstArgs,
+ // /*DefaultArguments=*/{},
+ // /*PartialTemplateArgs=*/true, CTAI))
+ // 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]) {
+ for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) {
+ TemplateArgument Arg;
+ if (Used[I])
// SubstitutedOuterMost[I].dump();
// SubstArgs[MappedIndex].getArgument().dump();
- TemplateArgument Arg = S.Context.getCanonicalTemplateArgument(
+ Arg = S.Context.getCanonicalTemplateArgument(
SubstArgs[MappedIndex++].getArgument());
if (I < SubstitutedOuterMost.size())
SubstitutedOuterMost[I] = Arg;
@@ -681,7 +689,7 @@ static bool calculateConstraintSatisfaction(
Ok = calculateConstraintSatisfaction(S, Constraint.getRHS(), Template,
TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
- if(Ok && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
+ if (Ok && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
Satisfaction.Details.erase(Satisfaction.Details.begin() + EffectiveDetailEndIndex,
Satisfaction.Details.end());
return Ok;
@@ -1590,7 +1598,7 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
/*OnlyDeduced=*/false,
/*Depth=*/0, OccurringIndices);
} else if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) {
- auto Args = static_cast<ConceptIdConstraint &>(N)
+ auto *Args = static_cast<ConceptIdConstraint &>(N)
.getConceptId()
->getTemplateArgsAsWritten();
if (Args)
@@ -1629,12 +1637,25 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
if (S.SubstTemplateArgumentsInParameterMapping(N.getParameterMapping(), MLTAL,
SubstArgs))
return true;
+ // Sema::CheckTemplateArgumentInfo CTAI;
+ // auto *TD =
+ // const_cast<TemplateDecl *>(cast<TemplateDecl>(N.getConstraintDecl()));
+ // if (S.CheckTemplateArgumentList(TD, TD->getLocation(), SubstArgs,
+ // /*DefaultArguments=*/{},
+ // /*PartialTemplateArgs=*/true, CTAI))
+ // return true;
TemplateArgumentLoc *TempArgs =
new (S.Context) TemplateArgumentLoc[SubstArgs.size()];
+ // for (unsigned I = 0; I < CTAI.SugaredConverted.size(); ++I)
+ // TempArgs[I] = S.getTrivialTemplateArgumentLoc(CTAI.SugaredConverted[I],
+ // QualType(), SourceLocation());
llvm::copy(SubstArgs.arguments(), TempArgs);
N.updateParameterMapping(
N.mappingOccurenceList(),
MutableArrayRef<TemplateArgumentLoc>(TempArgs, SubstArgs.size()));
+ // N.updateParameterMapping(N.mappingOccurenceList(),
+ // MutableArrayRef<TemplateArgumentLoc>(
+ // TempArgs, CTAI.SugaredConverted.size()));
return false;
}
@@ -1643,13 +1664,11 @@ 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);
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index dcac1e0b975b8..cc8474c576a3a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5972,6 +5972,17 @@ bool Sema::CheckTemplateArgumentList(
return true;
}
+ // For constraint parameter mapping, we have already built a pack in
+ // TransformTemplateArguments
+ // if (inParameterMappingSubstitution()) {
+ // llvm::copy(SugaredArgumentPack, std::back_inserter(CTAI.SugaredConverted));
+ // SugaredArgumentPack.clear();
+ // llvm::copy(CanonicalArgumentPack, std::back_inserter(CTAI.CanonicalConverted));
+ // CanonicalArgumentPack.clear();
+ // ++Param;
+ // continue;
+ // }
+
CTAI.SugaredConverted.push_back(
TemplateArgument::CreatePackCopy(Context, SugaredArgumentPack));
SugaredArgumentPack.clear();
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 2ea76cd08862b..1a1586a5052f3 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -7077,10 +7077,13 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
break;
case Type::UnaryTransform:
- if (!OnlyDeduced)
- MarkUsedTemplateParameters(Ctx,
- cast<UnaryTransformType>(T)->getUnderlyingType(),
- OnlyDeduced, Depth, Used);
+ if (!OnlyDeduced) {
+ auto *UTT = cast<UnaryTransformType>(T);
+ auto Next = UTT->getUnderlyingType();
+ if (Next.isNull())
+ Next = UTT->getBaseType();
+ MarkUsedTemplateParameters(Ctx, Next, OnlyDeduced, Depth, Used);
+ }
break;
case Type::PackExpansion:
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index f56194b81d987..a8892e9af57b0 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1526,7 +1526,10 @@ class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
}
bool ShouldPreserveTemplateArgumentsPacks() const {
- return PreserveArgumentPacks;
+ // This is disabled temporarily.
+ // We need to figure out a way to correctly handle packs outside of
+ // CheckTemplateArguments
+ return false && PreserveArgumentPacks;
}
TemplateArgument
@@ -2212,8 +2215,7 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
if (Arg.getKind() != TemplateArgument::Expression) {
assert(SemaRef.inParameterMappingSubstitution());
// FIXME: SourceLocation()?
- ExprResult E = SemaRef.BuildExpressionFromNonTypeTemplateArgument(
- Arg, SourceLocation());
+ ExprResult E = SemaRef.BuildExpressionFromNonTypeTemplateArgument(Arg, SourceLocation());
if (E.isInvalid())
return E;
Arg = TemplateArgument(E.get(), /*IsCanonical=*/false);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4c792c30e67e5..f1821b8a420eb 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7005,11 +7005,11 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType(
TypeLocBuilder &TLB,
UnaryTransformTypeLoc TL) {
QualType Result = TL.getType();
+ TypeSourceInfo *NewBaseTSI = TL.getUnderlyingTInfo();
if (Result->isDependentType()) {
const UnaryTransformType *T = TL.getTypePtr();
- TypeSourceInfo *NewBaseTSI =
- getDerived().TransformType(TL.getUnderlyingTInfo());
+ NewBaseTSI = getDerived().TransformType(TL.getUnderlyingTInfo());
if (!NewBaseTSI)
return QualType();
QualType NewBase = NewBaseTSI->getType();
@@ -7024,7 +7024,7 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType(
UnaryTransformTypeLoc NewTL = TLB.push<UnaryTransformTypeLoc>(Result);
NewTL.setKWLoc(TL.getKWLoc());
NewTL.setParensRange(TL.getParensRange());
- NewTL.setUnderlyingTInfo(TL.getUnderlyingTInfo());
+ NewTL.setUnderlyingTInfo(NewBaseTSI);
return Result;
}
>From 0652dcc9d821226827b3b048cd29167f1f0a4264 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 16 Jun 2025 19:18:17 +0800
Subject: [PATCH 16/31] Fix more ByteCode tests
Only 5 tests left now:
Failed Tests (5):
Clang :: CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
Clang :: Modules/pr62943.cppm
Clang :: SemaCXX/cxx2c-fold-exprs.cpp
Clang :: SemaTemplate/concepts-recursive-inst.cpp
Clang :: SemaTemplate/instantiate-template-argument.cpp
---
clang/include/clang/Sema/Sema.h | 9 +-
clang/include/clang/Sema/SemaConcept.h | 23 +++--
clang/lib/Sema/SemaConcept.cpp | 92 ++++++++++++-------
clang/lib/Sema/SemaTemplate.cpp | 16 +++-
clang/lib/Sema/SemaTemplateDeduction.cpp | 1 +
clang/lib/Sema/SemaTemplateInstantiate.cpp | 15 +--
.../instantiate-template-argument.cpp | 1 +
7 files changed, 104 insertions(+), 53 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index c8b86604ac183..59f0a8375dfd0 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11987,6 +11987,13 @@ class Sema final : public SemaBase {
bool UpdateArgsWithConversions = true,
bool *ConstraintsNotSatisfied = nullptr);
+ bool CheckTemplateArgumentList(
+ TemplateDecl *Template, TemplateParameterList *Params,
+ SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs,
+ const DefaultArguments &DefaultArgs, bool PartialTemplateArgs,
+ CheckTemplateArgumentInfo &CTAI, bool UpdateArgsWithConversions = true,
+ bool *ConstraintsNotSatisfied = nullptr);
+
bool CheckTemplateTypeArgument(
TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg,
SmallVectorImpl<TemplateArgument> &SugaredConverted,
@@ -13329,7 +13336,7 @@ class Sema final : public SemaBase {
TemplateArgumentListInfo &Outputs);
bool SubstTemplateArgumentsInParameterMapping(
- ArrayRef<TemplateArgumentLoc> Args,
+ ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Out);
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index 2f5be9d0b0325..29759a730863f 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -69,6 +69,7 @@ struct NormalizedConstraint {
unsigned PackSubstitutionIndex : 26;
llvm::SmallBitVector Indexes;
TemplateArgumentLoc *Args;
+ TemplateParameterList *ParamList;
ExprOrConcept ConstraintExpr;
const NamedDecl *ConstraintDecl;
};
@@ -81,6 +82,7 @@ struct NormalizedConstraint {
unsigned Placeholder : 26;
OccurenceList Indexes;
TemplateArgumentLoc *Args;
+ TemplateParameterList *ParamList;
const Expr *Pattern;
NormalizedConstraint *Constraint;
};
@@ -118,6 +120,7 @@ struct NormalizedConstraint {
PackIndex.toInternalRepresentation(),
/*Indexes=*/{},
/*Args=*/nullptr,
+ /*ParamList=*/nullptr,
ConstraintExpr,
ConstraintDecl} {}
@@ -128,6 +131,7 @@ struct NormalizedConstraint {
/*Placeholder=*/0,
/*Indexes=*/{},
/*Args=*/nullptr,
+ /*ParamList=*/nullptr,
Pattern,
Constraint} {}
@@ -138,7 +142,8 @@ struct NormalizedConstraint {
: ConceptId{{llvm::to_underlying(ConstraintKind::ConceptId),
/*Placeholder=*/0, PackIndex.toInternalRepresentation(),
/*Indexes=*/{},
- /*Args=*/nullptr, ConceptId, ConstraintDecl},
+ /*Args=*/nullptr, /*ParamList=*/nullptr, ConceptId,
+ ConstraintDecl},
SubConstraint} {}
NormalizedConstraint(NormalizedConstraint *LHS, CompoundConstraintKind CCK,
@@ -160,15 +165,18 @@ struct NormalizedConstraint {
return {Atomic.Args, Atomic.Indexes.count()};
}
- void InitParameterMapping(TemplateParameterList *TemplateParams, const Expr *,
- const ASTTemplateArgumentListInfo *ArgsAsWritten);
+ TemplateParameterList *getUsedTemplateParamList() const {
+ return Atomic.ParamList;
+ }
void updateParameterMapping(OccurenceList Indexes,
- llvm::MutableArrayRef<TemplateArgumentLoc> Args) {
+ llvm::MutableArrayRef<TemplateArgumentLoc> Args,
+ TemplateParameterList *ParamList) {
assert(getKind() != ConstraintKind::Compound);
assert(Indexes.count() == Args.size());
Atomic.Indexes = Indexes;
Atomic.Args = Args.data();
+ Atomic.ParamList = ParamList;
}
bool hasMatchingParameterMapping(ASTContext &C,
@@ -283,6 +291,7 @@ class NormalizedConstraintWithParamMapping : public NormalizedConstraint {
using NormalizedConstraint::hasParameterMapping;
using NormalizedConstraint::mappingOccurenceList;
using NormalizedConstraint::updateParameterMapping;
+ using NormalizedConstraint::getUsedTemplateParamList;
const NamedDecl *getConstraintDecl() const { return Atomic.ConstraintDecl; }
@@ -307,12 +316,6 @@ class AtomicConstraint : public NormalizedConstraintWithParamMapping {
const Expr *getConstraintExpr() const {
return cast<const Expr *>(Atomic.ConstraintExpr);
}
-
- void InitParameterMapping(const ASTTemplateArgumentListInfo *ArgsAsWritten) {
- NormalizedConstraint::InitParameterMapping(
- cast<TemplateDecl>(Atomic.ConstraintDecl)->getTemplateParameters(),
- getConstraintExpr(), ArgsAsWritten);
- }
};
class FoldExpandedConstraint : public NormalizedConstraint {
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index ba83408dff36d..6ef8ebaa9f8dd 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -377,16 +377,18 @@ SubstitutionInTemplateArguments(
? Constraint.getPackSubstitutionIndex()
: PackSubstitutionIndex);
if (S.SubstTemplateArgumentsInParameterMapping(
- Constraint.getParameterMapping(), MLTAL, SubstArgs) ||
+ Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL,
+ SubstArgs) ||
Trap.hasErrorOccurred())
return std::nullopt;
- // Sema::CheckTemplateArgumentInfo CTAI;
- // auto *TD = const_cast<TemplateDecl *>(
- // cast<TemplateDecl>(Constraint.getConstraintDecl()));
- // if (S.CheckTemplateArgumentList(TD, TD->getLocation(), SubstArgs,
- // /*DefaultArguments=*/{},
- // /*PartialTemplateArgs=*/true, CTAI))
- // return std::nullopt;
+ Sema::CheckTemplateArgumentInfo CTAI;
+ auto *TD = const_cast<TemplateDecl *>(
+ cast<TemplateDecl>(Constraint.getConstraintDecl()));
+ if (S.CheckTemplateArgumentList(TD, Constraint.getUsedTemplateParamList(),
+ TD->getLocation(), SubstArgs,
+ /*DefaultArguments=*/{},
+ /*PartialTemplateArgs=*/false, CTAI))
+ return std::nullopt;
NormalizedConstraint::OccurenceList Used =
Constraint.mappingOccurenceList();
SubstitutedOuterMost =
@@ -396,8 +398,10 @@ SubstitutionInTemplateArguments(
if (Used[I])
// SubstitutedOuterMost[I].dump();
// SubstArgs[MappedIndex].getArgument().dump();
+ // Arg = S.Context.getCanonicalTemplateArgument(
+ // SubstArgs[MappedIndex++].getArgument());
Arg = S.Context.getCanonicalTemplateArgument(
- SubstArgs[MappedIndex++].getArgument());
+ CTAI.SugaredConverted[MappedIndex++]);
if (I < SubstitutedOuterMost.size())
SubstitutedOuterMost[I] = Arg;
else
@@ -1607,17 +1611,28 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
}
TemplateArgumentLoc *TempArgs =
new (S.Context) TemplateArgumentLoc[OccurringIndices.count()];
+ llvm::SmallVector<NamedDecl *> UsedParams;
for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) {
SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I
? ArgsAsWritten->arguments()[I].getLocation()
: SourceLocation();
- if (OccurringIndices[I])
- new (&(TempArgs)[J++]) TemplateArgumentLoc(
- S.getIdentityTemplateArgumentLoc(TemplateParams->begin()[I], Loc));
+ if (OccurringIndices[I]) {
+ NamedDecl *Param = TemplateParams->begin()[I];
+ new (&(TempArgs)[J])
+ TemplateArgumentLoc(S.getIdentityTemplateArgumentLoc(Param, Loc));
+ UsedParams.push_back(Param);
+ J++;
+ }
}
+ auto *UsedList = TemplateParameterList::Create(
+ S.Context, TemplateParams->getTemplateLoc(),
+ TemplateParams->getLAngleLoc(), UsedParams,
+ /*RAngleLoc=*/SourceLocation(),
+ /*RequiresClause=*/nullptr);
N.updateParameterMapping(OccurringIndices,
MutableArrayRef<TemplateArgumentLoc>{
- TempArgs, OccurringIndices.count()});
+ TempArgs, OccurringIndices.count()},
+ UsedList);
}
SourceLocation InstLocBegin =
ArgsAsWritten->arguments().empty()
@@ -1634,28 +1649,39 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
{InstLocBegin, InstLocEnd});
if (Inst.isInvalid())
return true;
- if (S.SubstTemplateArgumentsInParameterMapping(N.getParameterMapping(), MLTAL,
- SubstArgs))
+ // TransformTemplateArguments is unable to preserve the source location of a
+ // pack. The SourceLocation is necessary for the instantiation location.
+ // FIXME: The BaseLoc will be used as the location of the pack expansion,
+ // which is wrong.
+ if (S.SubstTemplateArgumentsInParameterMapping(
+ N.getParameterMapping(), N.getBeginLoc(), MLTAL, SubstArgs))
+ return true;
+ Sema::CheckTemplateArgumentInfo CTAI;
+ auto *TD =
+ const_cast<TemplateDecl *>(cast<TemplateDecl>(N.getConstraintDecl()));
+ if (S.CheckTemplateArgumentList(TD, N.getUsedTemplateParamList(),
+ TD->getLocation(), SubstArgs,
+ /*DefaultArguments=*/{},
+ /*PartialTemplateArgs=*/false, CTAI))
return true;
- // Sema::CheckTemplateArgumentInfo CTAI;
- // auto *TD =
- // const_cast<TemplateDecl *>(cast<TemplateDecl>(N.getConstraintDecl()));
- // if (S.CheckTemplateArgumentList(TD, TD->getLocation(), SubstArgs,
- // /*DefaultArguments=*/{},
- // /*PartialTemplateArgs=*/true, CTAI))
- // return true;
TemplateArgumentLoc *TempArgs =
- new (S.Context) TemplateArgumentLoc[SubstArgs.size()];
- // for (unsigned I = 0; I < CTAI.SugaredConverted.size(); ++I)
- // TempArgs[I] = S.getTrivialTemplateArgumentLoc(CTAI.SugaredConverted[I],
- // QualType(), SourceLocation());
- llvm::copy(SubstArgs.arguments(), TempArgs);
- N.updateParameterMapping(
- N.mappingOccurenceList(),
- MutableArrayRef<TemplateArgumentLoc>(TempArgs, SubstArgs.size()));
- // N.updateParameterMapping(N.mappingOccurenceList(),
- // MutableArrayRef<TemplateArgumentLoc>(
- // TempArgs, CTAI.SugaredConverted.size()));
+ new (S.Context) TemplateArgumentLoc[CTAI.SugaredConverted.size()];
+ for (unsigned I = 0; I < CTAI.SugaredConverted.size(); ++I) {
+ SourceLocation Loc;
+ // If this is an empty pack, we have no corresponding SubstArgs.
+ if (I < SubstArgs.size())
+ Loc = SubstArgs.arguments()[I].getLocation();
+ TempArgs[I] = S.getTrivialTemplateArgumentLoc(CTAI.SugaredConverted[I],
+ QualType(), Loc);
+ }
+ // llvm::copy(SubstArgs.arguments(), TempArgs);
+ // N.updateParameterMapping(
+ // N.mappingOccurenceList(),
+ // MutableArrayRef<TemplateArgumentLoc>(TempArgs, SubstArgs.size()));
+ N.updateParameterMapping(N.mappingOccurenceList(),
+ MutableArrayRef<TemplateArgumentLoc>(
+ TempArgs, CTAI.SugaredConverted.size()),
+ N.getUsedTemplateParamList());
return false;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index cc8474c576a3a..4998ff81410ad 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5770,6 +5770,20 @@ bool Sema::CheckTemplateArgumentList(
TemplateArgumentListInfo &TemplateArgs, const DefaultArguments &DefaultArgs,
bool PartialTemplateArgs, CheckTemplateArgumentInfo &CTAI,
bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied) {
+ return CheckTemplateArgumentList(
+ Template, GetTemplateParameterList(Template), TemplateLoc, TemplateArgs,
+ DefaultArgs, PartialTemplateArgs, CTAI, UpdateArgsWithConversions,
+ ConstraintsNotSatisfied);
+}
+
+/// Check that the given template argument list is well-formed
+/// for specializing the given template.
+bool Sema::CheckTemplateArgumentList(
+ TemplateDecl *Template, TemplateParameterList *Params,
+ SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs,
+ const DefaultArguments &DefaultArgs, bool PartialTemplateArgs,
+ CheckTemplateArgumentInfo &CTAI, bool UpdateArgsWithConversions,
+ bool *ConstraintsNotSatisfied) {
if (ConstraintsNotSatisfied)
*ConstraintsNotSatisfied = false;
@@ -5779,8 +5793,6 @@ bool Sema::CheckTemplateArgumentList(
// template.
TemplateArgumentListInfo NewArgs = TemplateArgs;
- TemplateParameterList *Params = GetTemplateParameterList(Template);
-
SourceLocation RAngleLoc = NewArgs.getRAngleLoc();
// C++23 [temp.arg.general]p1:
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 1a1586a5052f3..ff873e85b07e8 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -6718,6 +6718,7 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor {
if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(E->getDecl()))
if (NTTP->getDepth() == Depth)
Used[NTTP->getIndex()] = true;
+ DynamicRecursiveASTVisitor::TraverseType(E->getType());
return true;
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index a8892e9af57b0..7bdfb4014b965 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1426,8 +1426,9 @@ class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
} ForParameterMappingSubstitution;
TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
+ SourceLocation Loc,
const MultiLevelTemplateArgumentList &TemplateArgs)
- : inherited(SemaRef), TemplateArgs(TemplateArgs),
+ : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
BailOutOnIncomplete(false), PreserveArgumentPacks(true) {}
void setEvaluateConstraints(bool B) { EvaluateConstraints = B; }
@@ -2214,11 +2215,11 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
Arg = getTemplateArgumentPackPatternForRewrite(Arg);
if (Arg.getKind() != TemplateArgument::Expression) {
assert(SemaRef.inParameterMappingSubstitution());
- // FIXME: SourceLocation()?
- ExprResult E = SemaRef.BuildExpressionFromNonTypeTemplateArgument(Arg, SourceLocation());
- if (E.isInvalid())
+ ExprResult Expr = SemaRef.BuildExpressionFromNonTypeTemplateArgument(
+ Arg, E->getLocation());
+ if (Expr.isInvalid())
return E;
- Arg = TemplateArgument(E.get(), /*IsCanonical=*/false);
+ Arg = TemplateArgument(Expr.get(), /*IsCanonical=*/false);
}
assert(Arg.getKind() == TemplateArgument::Expression &&
"unexpected nontype template argument kind in template rewrite");
@@ -4486,11 +4487,11 @@ bool Sema::SubstTemplateArguments(
}
bool Sema::SubstTemplateArgumentsInParameterMapping(
- ArrayRef<TemplateArgumentLoc> Args,
+ ArrayRef<TemplateArgumentLoc> Args, SourceLocation BaseLoc,
const MultiLevelTemplateArgumentList &TemplateArgs,
TemplateArgumentListInfo &Out) {
TemplateInstantiator Instantiator(
- TemplateInstantiator::ForParameterMappingSubstitution, *this,
+ TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc,
TemplateArgs);
return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
}
diff --git a/clang/test/SemaTemplate/instantiate-template-argument.cpp b/clang/test/SemaTemplate/instantiate-template-argument.cpp
index 43d5d00c8cb20..f65642b401bcd 100644
--- a/clang/test/SemaTemplate/instantiate-template-argument.cpp
+++ b/clang/test/SemaTemplate/instantiate-template-argument.cpp
@@ -17,6 +17,7 @@ template<char X>
constexpr int foo() requires C1<1, X> && true { return 2; }
// sizeof(U) >= 4 [U = X (decltype(1))]
+// GCC rejects it: https://godbolt.org/z/MWG756K8c
static_assert(foo<'a'>() == 2);
template<char Z>
>From 45f7aa7297ebbdbb85a085df87fca394c5106bc4 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 17 Jun 2025 19:26:29 +0800
Subject: [PATCH 17/31] Fix Modules/pr62943.cppm
Inspired by https://github.com/llvm/llvm-project/commit/72ac90715876e2963af899cbb883bad1e07b67bd
---
clang/lib/Sema/SemaConcept.cpp | 17 ++++++-----------
clang/lib/Serialization/ASTReaderStmt.cpp | 9 +++++++--
clang/lib/Serialization/ASTWriterStmt.cpp | 18 ++++++++++++------
3 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 6ef8ebaa9f8dd..0718d82a8175b 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1556,22 +1556,15 @@ void Sema::DiagnoseUnsatisfiedConstraint(
const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
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.
-
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(
+ auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
*this, ND, AssociatedConstraints);
CacheEntry =
NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
@@ -1811,10 +1804,12 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
// constraint. If any such substitution results in an invalid type or
// expression, the program is ill-formed; no diagnostic is required.
// [...]
- ConceptDecl *CD = CSE->getNamedConcept();
+
+ // Use canonical declarations to merge ConceptDecls across
+ // different modules.
+ ConceptDecl *CD = CSE->getNamedConcept()->getCanonicalDecl();
SubNF = NormalizedConstraint::fromAssociatedConstraints(
- S, CSE->getNamedConcept(),
- AssociatedConstraint(CD->getConstraintExpr(), SubstIndex));
+ S, CD, AssociatedConstraint(CD->getConstraintExpr(), SubstIndex));
if (!SubNF)
return nullptr;
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 3f37dfbc3dea9..e00611e6a25d6 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -802,15 +802,20 @@ readConstraintSatisfaction(ASTRecordReader &Record) {
if (!Satisfaction.IsSatisfied) {
unsigned NumDetailRecords = Record.readInt();
for (unsigned i = 0; i != NumDetailRecords; ++i) {
- if (/* IsDiagnostic */Record.readInt()) {
+ auto Kind = Record.readInt();
+ if (Kind == 0) {
SourceLocation DiagLocation = Record.readSourceLocation();
StringRef DiagMessage = C.backupStr(Record.readString());
Satisfaction.Details.emplace_back(
new (C) ConstraintSatisfaction::SubstitutionDiagnostic(
DiagLocation, DiagMessage));
- } else
+ } else if (Kind == 1) {
Satisfaction.Details.emplace_back(Record.readExpr());
+ } else {
+ assert(Kind == 2);
+ Satisfaction.Details.emplace_back(Record.readConceptReference());
+ }
}
}
return Satisfaction;
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index bf2902d8931f2..57c85b74b6b17 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -475,14 +475,20 @@ addConstraintSatisfaction(ASTRecordWriter &Record,
if (!Satisfaction.IsSatisfied) {
Record.push_back(Satisfaction.NumRecords);
for (const auto &DetailRecord : Satisfaction) {
- auto *E = dyn_cast<const Expr *>(DetailRecord);
- Record.push_back(/* IsDiagnostic */ E == nullptr);
- if (E)
- Record.AddStmt(const_cast<Expr *>(E));
- else {
- auto *Diag = cast<std::pair<SourceLocation, StringRef> *>(DetailRecord);
+ if (auto *Diag =
+ dyn_cast<std::pair<SourceLocation, StringRef> *>(DetailRecord)) {
+ Record.push_back(/*Kind=*/0);
Record.AddSourceLocation(Diag->first);
Record.AddString(Diag->second);
+ continue;
+ }
+ if (auto *E = dyn_cast<const Expr *>(DetailRecord)) {
+ Record.push_back(/*Kind=*/1);
+ Record.AddStmt(const_cast<Expr *>(E));
+ } else {
+ Record.push_back(/*Kind=*/2);
+ auto *CR = cast<const ConceptReference *>(DetailRecord);
+ Record.AddConceptReference(CR);
}
}
}
>From c2eeeef875a43e283b921a17d974818423a03499 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 25 Jun 2025 15:19:47 +0200
Subject: [PATCH 18/31] Fix a test case
the expressions don't subsume because they have different
parameter mappings
---
.../instantiate-template-argument.cpp | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/clang/test/SemaTemplate/instantiate-template-argument.cpp b/clang/test/SemaTemplate/instantiate-template-argument.cpp
index f65642b401bcd..74050acdf4f35 100644
--- a/clang/test/SemaTemplate/instantiate-template-argument.cpp
+++ b/clang/test/SemaTemplate/instantiate-template-argument.cpp
@@ -9,21 +9,24 @@ concept C2 = C1<Y{}, V>;
// sizeof(U) >= 4 [U = V (decltype(Y{}))]
template<char W>
-constexpr int foo() requires C2<int, W> { return 1; }
+constexpr int foo() requires C2<int, W> { return 1; } // #cand1
// sizeof(U) >= 4 [U = W (decltype(int{}))]
template<char X>
// expected-note at +1{{candidate function}}
-constexpr int foo() requires C1<1, X> && true { return 2; }
+constexpr int foo() requires C1<1, X> && true { return 2; } // #cand2
// sizeof(U) >= 4 [U = X (decltype(1))]
-// GCC rejects it: https://godbolt.org/z/MWG756K8c
static_assert(foo<'a'>() == 2);
+// expected-error at -1 {{call to 'foo' is ambiguous}}
+// expected-note@#cand1 {{candidate function}}
+// expected-note@#cand2 {{candidate function}}
template<char Z>
-// expected-note at +1{{candidate function}}
-constexpr int foo() requires C2<long long, Z> && true { return 3; }
+constexpr int foo() requires C2<long long, Z> && true { return 3; } // #cand3
// sizeof(U) >= 4 [U = Z (decltype(long long{}))]
static_assert(foo<'a'>() == 3);
-// expected-error at -1{{call to 'foo' is ambiguous}}
\ No newline at end of file
+// expected-error at -1{{call to 'foo' is ambiguous}}
+// expected-note@#cand1 {{candidate function}}
+// expected-note@#cand3 {{candidate function}}
>From 113d9d984df956516f834db344166cae08996503 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 25 Jun 2025 15:51:54 +0200
Subject: [PATCH 19/31] add test for #135190
---
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 43 +++++++++++++++++++++++++
clang/test/SemaTemplate/concepts.cpp | 9 ++++++
2 files changed, 52 insertions(+)
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index fb2fc95949dab..63680c1fed6d1 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -385,3 +385,46 @@ struct LazyLitMatrix<index_by<Indices...>, init> {
}
}
+
+namespace GH135190 {
+template <typename T>
+concept A = __is_same_as(T, int) || __is_same_as(T, double) ;
+
+template <typename T>
+concept B = A<T> && __is_same_as(T, double);
+
+template <class... Ts>
+requires(A<Ts> && ...)
+constexpr int g() {
+ return 1;
+}
+
+template <class... Ts>
+requires(B<Ts> && ...)
+constexpr int g() {
+ return 2;
+}
+
+static_assert(g<double>() == 2);
+
+
+template <class... Ts>
+concept all_A = (A<Ts> && ...);
+
+template <class... Ts>
+concept all_B = (B<Ts> && ...);
+
+template <class... Ts>
+requires all_A<Ts...>
+constexpr int h() {
+ return 1;
+}
+
+template <class... Ts>
+requires all_B<Ts...>
+constexpr int h() {
+ return 2;
+}
+
+static_assert(h<double>() == 2);
+}
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 7148790f87ef9..30decb8ce7a67 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1258,4 +1258,13 @@ template <typename T> concept PerfectSquare = [](){} // expected-note 2{{here}}
([](auto) { return true; }) < PerfectSquare <class T>;
// expected-error at -1 {{declaration of 'T' shadows template parameter}} \
// expected-error at -1 {{a concept definition cannot refer to itself}}
+
+}
+namespace GH61811{
+template <class T> struct A { static const int x = 42; };
+template <class Ta> concept A42 = A<Ta>::x == 42;
+template <class Tv> concept Void = __is_same_as(Tv, void);
+template <class Tb, class Ub> concept A42b = Void<Tb> || A42<Ub>;
+template <class Tc> concept R42c = A42b<Tc, Tc&>;
+static_assert (R42c<void>);
}
>From 1227a5d9dd120dc7d991c2a5d698a8f6393bc521 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 26 Jun 2025 11:55:16 +0200
Subject: [PATCH 20/31] cleanup fold expression tests
---
clang/lib/Sema/SemaConcept.cpp | 22 +++-----
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 67 +++++++++++--------------
clang/test/SemaTemplate/concepts.cpp | 6 +--
3 files changed, 39 insertions(+), 56 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 0718d82a8175b..a9b2ca0640aab 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -367,9 +367,6 @@ SubstitutionInTemplateArguments(
Sema::SFINAETrap Trap(S);
- // TODO substitute at the appropriate depth
- // Template->getTemplateDepth();
-
TemplateArgumentListInfo SubstArgs;
if (Constraint.hasParameterMapping()) {
Sema::ArgPackSubstIndexRAII SubstIndex(
@@ -396,10 +393,6 @@ SubstitutionInTemplateArguments(
for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) {
TemplateArgument Arg;
if (Used[I])
- // SubstitutedOuterMost[I].dump();
- // SubstArgs[MappedIndex].getArgument().dump();
- // Arg = S.Context.getCanonicalTemplateArgument(
- // SubstArgs[MappedIndex++].getArgument());
Arg = S.Context.getCanonicalTemplateArgument(
CTAI.SugaredConverted[MappedIndex++]);
if (I < SubstitutedOuterMost.size())
@@ -671,21 +664,20 @@ static bool calculateConstraintSatisfaction(
auto EffectiveDetailEndIndex = Satisfaction.Details.size();
+ bool Conjunction = Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction;
+
bool Ok = calculateConstraintSatisfaction(
S, Constraint.getLHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
- if (!Ok || Satisfaction.ContainsErrors)
- return Ok;
+ if(Conjunction && !Ok)
+ return false;
- if (Satisfaction.IsSatisfied &&
- Constraint.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) {
+ if (!Conjunction && Ok && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
return true;
- }
- if (!Satisfaction.IsSatisfied &&
- Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction) {
+
+ if (Conjunction && Ok && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
return true;
- }
Satisfaction.ContainsErrors = false;
Satisfaction.IsSatisfied = false;
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index 63680c1fed6d1..a2a5f1e413dcf 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -40,13 +40,23 @@ constexpr int i(T...) { return 1; }; // expected-note {{candidate}}
static_assert(i(0) == 1); // expected-error {{call to 'i' is ambiguous}}
-template <class... T> requires (A<T> || ... || true)
-constexpr int j(T...) { return 0; };
-template <class... T> requires (C<T> && ... && true)
-constexpr int j(T...) { return 1; };
+template <class... T> requires (A<T> || ... || true) constexpr int j(T...) { return 0; }; // #j1
+template <class... T> requires (C<T> && ... && true) constexpr int j(T...) { return 1; }; // #j2
static_assert(j(0) == 1);
+// expected-error at -1 {{call to 'j' is ambiguous}}
+// expected-note@#j1 {{candidate function [with T = <int>]}}
+// expected-note@#j2 {{candidate function [with T = <int>]}}
+// expected-note@#j2 {{imilar constraint expressions not considered equivalent}}
+// expected-note@#j1 {{similar constraint expression here}}
+
+
static_assert(j() == 1);
+// expected-error at -1 {{call to 'j' is ambiguous}}
+// expected-note@#j1 {{candidate function [with T = <>]}}
+// expected-note@#j2 {{candidate function [with T = <>]}}
+// expected-note@#j2 {{imilar constraint expressions not considered equivalent}}
+// expected-note@#j1 {{similar constraint expression here}}
@@ -145,51 +155,41 @@ static_assert(And1<>() == 1);
static_assert(And1<S>() == 1);
static_assert(And1<S, S>() == 1);
static_assert(And1<int>() == 1); // expected-error {{no matching function for call to 'And1'}}
- // expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and1 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and1 {{candidate template ignored: failed template argument deduction}}
static_assert(And1<S, int>() == 1); // expected-error {{no matching function for call to 'And1'}}
- // expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and1 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and1 {{candidate template ignored: failed template argument deduction}}
static_assert(And1<int, S>() == 1); // expected-error {{no matching function for call to 'And1'}}
- // expected-note@#and1 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and1 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and1 {{candidate template ignored: failed template argument deduction}}
static_assert(And2<S>() == 2);
static_assert(And2<S, S>() == 2);
-// FIXME: Should it compile??
-static_assert(And2<int>() == 2);
+static_assert(And2<int>() == 2); // expected-error {{no matching function for call to 'And2'}} \
+ // expected-note@#and2 {{candidate template ignored: failed template argument deduction}}
static_assert(And2<int, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
- // expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and2 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and2 {{candidate template ignored: failed template argument deduction}}
static_assert(And2<S, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
- // expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and2 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and2 {{candidate template ignored: failed template argument deduction}}
static_assert(And2<int, S>() == 2); // expected-error {{no matching function for call to 'And2'}}
- // expected-note@#and2 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and2 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and2 {{candidate template ignored: failed template argument deduction}}
static_assert(And3<S>() == 3);
static_assert(And3<S, S>() == 3);
static_assert(And3<int>() == 3); // expected-error {{no matching function for call to 'And3'}}
- // expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and3 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and3 {{candidate template ignored: failed template argument deduction}}
static_assert(And3<int, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
- // expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and3 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and3 {{candidate template ignored: failed template argument deduction}}
static_assert(And3<S, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
- // expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and3 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and3 {{candidate template ignored: failed template argument deduction}}
static_assert(And3<int, S>() == 3); // expected-error {{no matching function for call to 'And3'}}
- // expected-note@#and3 {{candidate template ignored: constraints not satisfied}}
- // expected-note@#and3 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#and3 {{candidate template ignored: failed template argument deduction}}
static_assert(Or1<>() == 1); // expected-error {{no matching function for call to 'Or1'}}
@@ -199,25 +199,20 @@ static_assert(Or1<int, S>() == 1);
static_assert(Or1<S, int>() == 1);
static_assert(Or1<S, S>() == 1);
static_assert(Or1<int>() == 1); // expected-error {{no matching function for call to 'Or1'}}
- // expected-note@#or1 {{candidate template ignored: constraints not satisfied}} \
- // expected-note@#or1 {{because substituted constraint expression is ill-formed}}
-
+ // expected-note@#or1 {{candidate template ignored: constraints not satisfied}}
static_assert(Or2<S>() == 2);
static_assert(Or2<int, S>() == 2);
static_assert(Or2<S, int>() == 2);
static_assert(Or2<S, S>() == 2);
static_assert(Or2<int>() == 2); // expected-error {{no matching function for call to 'Or2'}}
- // expected-note@#or2 {{candidate template ignored: constraints not satisfied}} \
- // expected-note@#or2 {{because substituted constraint expression is ill-formed}}
-
+ // expected-note@#or2 {{candidate template ignored: failed template argument deduction}}
static_assert(Or3<S>() == 3);
static_assert(Or3<int, S>() == 3);
static_assert(Or3<S, int>() == 3);
static_assert(Or3<S, S>() == 3);
static_assert(Or3<int>() == 3); // expected-error {{no matching function for call to 'Or3'}}
- // expected-note@#or3 {{candidate template ignored: constraints not satisfied}} \
- // expected-note@#or3 {{because substituted constraint expression is ill-formed}}
+ // expected-note@#or3 {{candidate template ignored: constraints not satisfied}}
}
namespace bool_conversion_break {
@@ -270,9 +265,7 @@ struct S {
static_assert(S<int>::f<int>() == 2);
-static_assert(S<int>::g<int>() == 2); // expected-error {{call to 'g' is ambiguous}}
- // expected-note@#nested-ambiguous-g1 {{candidate}}
- // expected-note@#nested-ambiguous-g2 {{candidate}}
+static_assert(S<int>::g<int>() == 2);
}
diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 30decb8ce7a67..98adb64b147bd 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -996,7 +996,7 @@ template<class>
concept True = true;
template<class>
-concept False = false; // expected-note 8 {{'false' evaluated to false}}
+concept False = false; // expected-note 9 {{'false' evaluated to false}}
template<class>
concept Irrelevant = false;
@@ -1023,9 +1023,7 @@ template<class T> void eee(T t) // expected-note {{candidate template ignored: c
requires (Irrelevant<T> || Irrelevant<T> || True<T>) && False<T> {} // expected-note {{'long' does not satisfy 'False'}}
template<class T> void fff(T t) // expected-note {{candidate template ignored: constraints not satisfied}}
-requires((ErrorRequires<T> || False<T> || True<T>) && False<T>) {} // expected-note {{because 'unsigned long' does not satisfy 'ErrorRequires'}}
-// // expected-note@#GH54678-ill-formed-concept {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
-
+requires((ErrorRequires<T> || False<T> || True<T>) && False<T>) {} // expected-note {{because 'unsigned long' does not satisfy 'False'}}
void test() {
aaa(42); // expected-error {{no matching function}}
bbb(42L); // expected-error{{no matching function}}
>From 502ce79f763207f049eb3c1b80ee9f4a12b88b56 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 26 Jun 2025 12:01:49 +0200
Subject: [PATCH 21/31] Format
---
clang/include/clang/Sema/SemaConcept.h | 2 +-
clang/lib/Sema/SemaConcept.cpp | 26 ++++++++++++++++----------
clang/lib/Sema/SemaTemplate.cpp | 6 ++++--
3 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index 29759a730863f..9f19ed0458b12 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -287,11 +287,11 @@ class NormalizedConstraintWithParamMapping : public NormalizedConstraint {
public:
using NormalizedConstraint::getParameterMapping;
+ using NormalizedConstraint::getUsedTemplateParamList;
using NormalizedConstraint::hasMatchingParameterMapping;
using NormalizedConstraint::hasParameterMapping;
using NormalizedConstraint::mappingOccurenceList;
using NormalizedConstraint::updateParameterMapping;
- using NormalizedConstraint::getUsedTemplateParamList;
const NamedDecl *getConstraintDecl() const { return Atomic.ConstraintDecl; }
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index a9b2ca0640aab..f0baf33fcb973 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -549,7 +549,7 @@ static bool calculateConstraintSatisfaction(
if (!NumExpansions)
return false;
- if(*NumExpansions == 0) {
+ if (*NumExpansions == 0) {
Satisfaction.IsSatisfied = Conjunction;
return true;
}
@@ -566,7 +566,8 @@ static bool calculateConstraintSatisfaction(
if (!Success && Conjunction)
return false;
if (!Conjunction && Satisfaction.IsSatisfied) {
- Satisfaction.Details.erase(Satisfaction.Details.begin() + EffectiveDetailEndIndex,
+ Satisfaction.Details.erase(Satisfaction.Details.begin() +
+ EffectiveDetailEndIndex,
Satisfaction.Details.end());
break;
}
@@ -664,19 +665,22 @@ static bool calculateConstraintSatisfaction(
auto EffectiveDetailEndIndex = Satisfaction.Details.size();
- bool Conjunction = Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction;
+ bool Conjunction =
+ Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction;
bool Ok = calculateConstraintSatisfaction(
S, Constraint.getLHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
- if(Conjunction && !Ok)
+ if (Conjunction && !Ok)
return false;
- if (!Conjunction && Ok && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
+ if (!Conjunction && Ok && Satisfaction.IsSatisfied &&
+ !Satisfaction.ContainsErrors)
return true;
- if (Conjunction && Ok && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
+ if (Conjunction && Ok &&
+ (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
return true;
Satisfaction.ContainsErrors = false;
@@ -686,7 +690,8 @@ static bool calculateConstraintSatisfaction(
TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
if (Ok && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
- Satisfaction.Details.erase(Satisfaction.Details.begin() + EffectiveDetailEndIndex,
+ Satisfaction.Details.erase(Satisfaction.Details.begin() +
+ EffectiveDetailEndIndex,
Satisfaction.Details.end());
return Ok;
}
@@ -1397,9 +1402,10 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
concepts::NestedRequirement *Req,
bool First) {
DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(),
- Req->hasInvalidConstraint() ? SourceLocation() :
- Req->getConstraintExpr()->getExprLoc(), First,
- Req);
+ Req->hasInvalidConstraint()
+ ? SourceLocation()
+ : Req->getConstraintExpr()->getExprLoc(),
+ First, Req);
}
static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 4998ff81410ad..256223e06873c 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5987,9 +5987,11 @@ bool Sema::CheckTemplateArgumentList(
// For constraint parameter mapping, we have already built a pack in
// TransformTemplateArguments
// if (inParameterMappingSubstitution()) {
- // llvm::copy(SugaredArgumentPack, std::back_inserter(CTAI.SugaredConverted));
+ // llvm::copy(SugaredArgumentPack,
+ // std::back_inserter(CTAI.SugaredConverted));
// SugaredArgumentPack.clear();
- // llvm::copy(CanonicalArgumentPack, std::back_inserter(CTAI.CanonicalConverted));
+ // llvm::copy(CanonicalArgumentPack,
+ // std::back_inserter(CTAI.CanonicalConverted));
// CanonicalArgumentPack.clear();
// ++Param;
// continue;
>From 3cb8a9f709dd19fb5ef704fd59502d354fd5dea7 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 1 Jul 2025 19:02:39 +0800
Subject: [PATCH 22/31] Add parameter mapping cache
This helps reduce the time cost of time_zone.cpp from 44s -> 14s
It still needs improvements, as previous clang only takes 3s.
---
clang/include/clang/Sema/Sema.h | 2 ++
clang/lib/Sema/SemaConcept.cpp | 34 ++++++++++++++++++++++++++++++---
2 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 59f0a8375dfd0..60f1378191c2d 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14817,6 +14817,8 @@ class Sema final : public SemaBase {
const NamedDecl *D1, ArrayRef<AssociatedConstraint> AC1,
const NamedDecl *D2, ArrayRef<AssociatedConstraint> AC2);
+ llvm::DenseMap<unsigned, MutableArrayRef<TemplateArgumentLoc>>
+ ParameterMappingCache;
private:
/// Caches pairs of template-like decls whose associated constraints were
/// checked for subsumption and whether or not the first's constraints did in
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index f0baf33fcb973..afa664d628796 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1558,6 +1558,7 @@ const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
return NormalizedConstraint::fromAssociatedConstraints(
*this, nullptr, AssociatedConstraints);
+ // FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement!
const NamedDecl *ND =
ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>();
auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq);
@@ -1640,6 +1641,31 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
{InstLocBegin, InstLocEnd});
if (Inst.isInvalid())
return true;
+
+ unsigned Hash;
+ llvm::FoldingSetNodeID ID;
+ auto &Context = S.getASTContext();
+ if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) {
+ ID.AddPointer(static_cast<ConceptIdConstraint &>(N)
+ .getConceptId()
+ ->getNamedConcept()
+ ->getCanonicalDecl());
+ for (auto &ArgLoc : static_cast<ConceptIdConstraint &>(N)
+ .getConceptId()
+ ->getTemplateArgsAsWritten()
+ ->arguments())
+ ArgLoc.getArgument().Profile(ID, Context);
+
+ Hash = ID.ComputeHash();
+ if (auto Iter = S.ParameterMappingCache.find(Hash);
+ Iter != S.ParameterMappingCache.end()) {
+ N.updateParameterMapping(N.mappingOccurenceList(), Iter->second,
+ N.getUsedTemplateParamList());
+ return false;
+ }
+ }
+ // FIXME: Cache for atomic constraints.
+
// TransformTemplateArguments is unable to preserve the source location of a
// pack. The SourceLocation is necessary for the instantiation location.
// FIXME: The BaseLoc will be used as the location of the pack expansion,
@@ -1669,10 +1695,12 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
// N.updateParameterMapping(
// N.mappingOccurenceList(),
// MutableArrayRef<TemplateArgumentLoc>(TempArgs, SubstArgs.size()));
- N.updateParameterMapping(N.mappingOccurenceList(),
- MutableArrayRef<TemplateArgumentLoc>(
- TempArgs, CTAI.SugaredConverted.size()),
+ MutableArrayRef<TemplateArgumentLoc> Mapping(TempArgs,
+ CTAI.SugaredConverted.size());
+ N.updateParameterMapping(N.mappingOccurenceList(), Mapping,
N.getUsedTemplateParamList());
+ if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId)
+ S.ParameterMappingCache.insert({Hash, Mapping});
return false;
}
>From 0cf03fb6ad558b0670b69813f90582f22cbab7c2 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Thu, 3 Jul 2025 20:23:18 +0800
Subject: [PATCH 23/31] Save the checkpoint
There are 6 tests failing now!
Failed Tests (6):
Clang :: AST/ByteCode/libcxx/deref-to-array.cpp
Clang :: AST/ByteCode/libcxx/primitive-temporary.cpp
Clang :: CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
Clang :: CXX/temp/temp.constr/temp.constr.normal/p1.cpp
Clang :: Modules/GH60336.cpp
Clang :: SemaTemplate/concepts-recursive-inst.cpp
---
clang/include/clang/Sema/Sema.h | 3 +-
clang/include/clang/Sema/SemaConcept.h | 14 +-
clang/lib/Sema/SemaConcept.cpp | 158 +++++++++++-------
.../temp.constr/temp.constr.normal/p1.cpp | 2 +
4 files changed, 115 insertions(+), 62 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 60f1378191c2d..59a396616a438 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -65,6 +65,7 @@
#include "clang/Sema/Redeclaration.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/SemaBase.h"
+#include "clang/Sema/SemaConcept.h"
#include "clang/Sema/TypoCorrection.h"
#include "clang/Sema/Weak.h"
#include "llvm/ADT/APInt.h"
@@ -14817,8 +14818,6 @@ class Sema final : public SemaBase {
const NamedDecl *D1, ArrayRef<AssociatedConstraint> AC1,
const NamedDecl *D2, ArrayRef<AssociatedConstraint> AC2);
- llvm::DenseMap<unsigned, MutableArrayRef<TemplateArgumentLoc>>
- ParameterMappingCache;
private:
/// Caches pairs of template-like decls whose associated constraints were
/// checked for subsumption and whether or not the first's constraints did in
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index 9f19ed0458b12..a03620b36caec 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -89,6 +89,9 @@ struct NormalizedConstraint {
struct ConceptIdBits : AtomicBits {
NormalizedConstraint *Sub;
+
+ // Only used for parameter mapping.
+ const ConceptSpecializationExpr *CSE;
};
struct CompoundBits {
@@ -138,13 +141,15 @@ struct NormalizedConstraint {
NormalizedConstraint(const ConceptReference *ConceptId,
const NamedDecl *ConstraintDecl,
NormalizedConstraint *SubConstraint,
+ const ConceptSpecializationExpr *CSE,
UnsignedOrNone PackIndex)
: ConceptId{{llvm::to_underlying(ConstraintKind::ConceptId),
/*Placeholder=*/0, PackIndex.toInternalRepresentation(),
/*Indexes=*/{},
/*Args=*/nullptr, /*ParamList=*/nullptr, ConceptId,
ConstraintDecl},
- SubConstraint} {}
+ SubConstraint,
+ CSE} {}
NormalizedConstraint(NormalizedConstraint *LHS, CompoundConstraintKind CCK,
NormalizedConstraint *RHS)
@@ -357,9 +362,14 @@ class ConceptIdConstraint : public NormalizedConstraintWithParamMapping {
const ConceptReference *ConceptId,
NormalizedConstraint *SubConstraint,
const NamedDecl *ConstraintDecl,
+ const ConceptSpecializationExpr *CSE,
UnsignedOrNone PackIndex) {
return new (Ctx) ConceptIdConstraint(ConceptId, ConstraintDecl,
- SubConstraint, PackIndex);
+ SubConstraint, CSE, PackIndex);
+ }
+
+ const ConceptSpecializationExpr *getConceptSpecializationExpr() const {
+ return ConceptId.CSE;
}
const ConceptReference *getConceptId() const {
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index afa664d628796..e41b68e3fbae3 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -35,6 +35,8 @@
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/WithColor.h"
#include <cstddef>
#include <optional>
@@ -390,16 +392,23 @@ SubstitutionInTemplateArguments(
Constraint.mappingOccurenceList();
SubstitutedOuterMost =
llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
+ unsigned Offset = 0;
for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) {
TemplateArgument Arg;
if (Used[I])
Arg = S.Context.getCanonicalTemplateArgument(
CTAI.SugaredConverted[MappedIndex++]);
- if (I < SubstitutedOuterMost.size())
+ if (I < SubstitutedOuterMost.size()) {
SubstitutedOuterMost[I] = Arg;
- else
+ Offset = I + 1;
+ } else {
SubstitutedOuterMost.push_back(Arg);
+ Offset = SubstitutedOuterMost.size();
}
+ }
+ if (Offset < SubstitutedOuterMost.size())
+ SubstitutedOuterMost.erase(SubstitutedOuterMost.begin() + Offset);
+
MLTAL.replaceOutermostTemplateArguments(
const_cast<NamedDecl *>(Constraint.getConstraintDecl()),
SubstitutedOuterMost);
@@ -765,7 +774,9 @@ static bool CheckConstraintSatisfaction(
if (TopLevelConceptId)
C = ConceptIdConstraint::Create(S.getASTContext(), TopLevelConceptId,
const_cast<NormalizedConstraint *>(C),
- Template, S.ArgPackSubstIndex);
+ Template, /*CSE=*/nullptr,
+ S.ArgPackSubstIndex);
+ }
ExprResult Res = calculateConstraintSatisfaction(
S, *C, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
@@ -1551,27 +1562,6 @@ void Sema::DiagnoseUnsatisfiedConstraint(
ConstraintExpr->getBeginLoc(), First);
}
-const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
- ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq,
- ArrayRef<AssociatedConstraint> AssociatedConstraints) {
- if (!ConstrainedDeclOrNestedReq)
- return NormalizedConstraint::fromAssociatedConstraints(
- *this, nullptr, AssociatedConstraints);
-
- // FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement!
- const NamedDecl *ND =
- ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>();
- auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq);
- if (CacheEntry == NormalizationCache.end()) {
- auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
- *this, ND, AssociatedConstraints);
- CacheEntry =
- NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
- .first;
- }
- return CacheEntry->second;
-}
-
static bool
substituteParameterMappings(Sema &S, NormalizedConstraint &N,
const MultiLevelTemplateArgumentList &MLTAL,
@@ -1642,30 +1632,6 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
if (Inst.isInvalid())
return true;
- unsigned Hash;
- llvm::FoldingSetNodeID ID;
- auto &Context = S.getASTContext();
- if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) {
- ID.AddPointer(static_cast<ConceptIdConstraint &>(N)
- .getConceptId()
- ->getNamedConcept()
- ->getCanonicalDecl());
- for (auto &ArgLoc : static_cast<ConceptIdConstraint &>(N)
- .getConceptId()
- ->getTemplateArgsAsWritten()
- ->arguments())
- ArgLoc.getArgument().Profile(ID, Context);
-
- Hash = ID.ComputeHash();
- if (auto Iter = S.ParameterMappingCache.find(Hash);
- Iter != S.ParameterMappingCache.end()) {
- N.updateParameterMapping(N.mappingOccurenceList(), Iter->second,
- N.getUsedTemplateParamList());
- return false;
- }
- }
- // FIXME: Cache for atomic constraints.
-
// TransformTemplateArguments is unable to preserve the source location of a
// pack. The SourceLocation is necessary for the instantiation location.
// FIXME: The BaseLoc will be used as the location of the pack expansion,
@@ -1699,8 +1665,6 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
CTAI.SugaredConverted.size());
N.updateParameterMapping(N.mappingOccurenceList(), Mapping,
N.getUsedTemplateParamList());
- if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId)
- S.ParameterMappingCache.insert({Hash, Mapping});
return false;
}
@@ -1708,14 +1672,39 @@ static bool
substituteParameterMappings(Sema &S, ConceptIdConstraint &N,
const MultiLevelTemplateArgumentList &MLTAL,
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
-
- if (N.getConstraintDecl()) {
- substituteParameterMappings(
+ assert(N.getConstraintDecl());
+#if 0
+ return substituteParameterMappings(
S, static_cast<NormalizedConstraintWithParamMapping &>(N), MLTAL,
ArgsAsWritten);
+#else
+ auto TemplateArgs = MLTAL;
+ if (N.getConstraintDecl()) {
+ if (substituteParameterMappings(
+ S, static_cast<NormalizedConstraintWithParamMapping &>(N),
+ TemplateArgs, ArgsAsWritten))
+ return true;
+ auto *CSE = N.getConceptSpecializationExpr();
+ assert(CSE);
+ TemplateArgumentListInfo Out;
+ assert(!N.getBeginLoc().isInvalid());
+ if (S.SubstTemplateArgumentsInParameterMapping(
+ CSE->getTemplateArgsAsWritten()->arguments(), N.getBeginLoc(),
+ MLTAL, Out))
+ return true;
+ Sema::CheckTemplateArgumentInfo CTAI;
+ if (S.CheckTemplateArgumentList(CSE->getNamedConcept(),
+ CSE->getConceptNameInfo().getLoc(), Out,
+ /*DefaultArgs=*/{},
+ /*PartialTemplateArgs=*/false, CTAI,
+ /*UpdateArgsWithConversions=*/false))
+ return true;
+ TemplateArgs.replaceOutermostTemplateArguments(
+ TemplateArgs.getAssociatedDecl(0).first, CTAI.CanonicalConverted);
}
- return substituteParameterMappings(S, N.getNormalizedConstraint(), MLTAL,
- ArgsAsWritten);
+ return substituteParameterMappings(S, N.getNormalizedConstraint(),
+ TemplateArgs, ArgsAsWritten);
+#endif
}
static bool
@@ -1758,10 +1747,40 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
// Don't build Subst* nodes to model lambda expressions.
// The transform of Subst* is oblivious to the lambda type.
MLTAL.setKind(TemplateSubstitutionKind::Rewrite);
+ Sema::InstantiatingTemplate Inst(
+ S, N.getBeginLoc(),
+ Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
+ CSE->getNamedConcept(), {N.getBeginLoc(), N.getEndLoc()});
+ if (Inst.isInvalid())
+ return true;
+
return substituteParameterMappings(S, N, MLTAL,
CSE->getTemplateArgsAsWritten());
}
+static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N) {
+ switch (N.getKind()) {
+ case NormalizedConstraint::ConstraintKind::Atomic:
+ return false;
+ case NormalizedConstraint::ConstraintKind::FoldExpanded: {
+ Sema::ArgPackSubstIndexRAII _(S, std::nullopt);
+ return substituteParameterMappings(
+ S, static_cast<FoldExpandedConstraint &>(N).getNormalizedPattern());
+ }
+ case NormalizedConstraint::ConstraintKind::ConceptId: {
+ auto &CC = static_cast<ConceptIdConstraint &>(N);
+ return substituteParameterMappings(S, CC.getNormalizedConstraint(),
+ CC.getConceptSpecializationExpr());
+ }
+ case NormalizedConstraint::ConstraintKind::Compound: {
+ auto &Compound = static_cast<CompoundConstraint &>(N);
+ if (substituteParameterMappings(S, Compound.getLHS()))
+ return true;
+ return substituteParameterMappings(S, Compound.getRHS());
+ }
+ }
+}
+
NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints(
Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) {
assert(ACs.size() != 0);
@@ -1840,11 +1859,11 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
if (!SubNF)
return nullptr;
}
- if (substituteParameterMappings(S, *SubNF, CSE))
- return nullptr;
+ // if (substituteParameterMappings(S, *SubNF, CSE))
+ // return nullptr;
return ConceptIdConstraint::Create(
- S.getASTContext(), CSE->getConceptReference(), SubNF, D, SubstIndex);
+ S.getASTContext(), CSE->getConceptReference(), SubNF, D, CSE, SubstIndex);
} else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
FE && S.getLangOpts().CPlusPlus26 &&
@@ -1886,6 +1905,29 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
return AtomicConstraint::Create(S.getASTContext(), E, D, SubstIndex);
}
+const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
+ ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq,
+ ArrayRef<AssociatedConstraint> AssociatedConstraints) {
+ if (!ConstrainedDeclOrNestedReq)
+ return NormalizedConstraint::fromAssociatedConstraints(
+ *this, nullptr, AssociatedConstraints);
+
+ // FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement!
+ const NamedDecl *ND =
+ ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>();
+ auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq);
+ if (CacheEntry == NormalizationCache.end()) {
+ auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
+ *this, ND, AssociatedConstraints);
+ CacheEntry =
+ NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
+ .first;
+ if (!Normalized || substituteParameterMappings(*this, *Normalized))
+ return nullptr;
+ }
+ return CacheEntry->second;
+}
+
bool FoldExpandedConstraint::AreCompatibleForSubsumption(
const FoldExpandedConstraint &A, const FoldExpandedConstraint &B) {
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 78f94f15eff05..62548b2c55a9b 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
@@ -1,9 +1,11 @@
// RUN: %clang_cc1 -std=c++2a -x c++ -verify %s
+// FIXME: RUN: %clang_cc1 -std=c++2c -x c++ -verify %s
template<typename T> concept True = true;
template<typename T> concept Foo = True<T*>;
template<typename T> concept Bar = Foo<T&>;
template<typename T> requires Bar<T> struct S { };
+// FIXME: GCC rejects: https://gcc.godbolt.org/z/c9G7G6PTx if the specialization is present.
template<typename T> requires Bar<T> && true struct S<T> { };
template<typename T> concept True2 = sizeof(T) >= 0;
>From 21cb63ba7302973bac73780dcaea97bb6af25808 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 4 Jul 2025 17:31:31 +0800
Subject: [PATCH 24/31] Save the checkpoint
Failed Tests (3):
Clang :: CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
Clang :: CXX/temp/temp.constr/temp.constr.normal/p1.cpp
Clang :: SemaTemplate/concepts-recursive-inst.cpp
---
clang/lib/Sema/SemaConcept.cpp | 43 +++++++++++++++++++++++++++++++---
clang/lib/Sema/TreeTransform.h | 4 ----
2 files changed, 40 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index e41b68e3fbae3..36d01485ea580 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DependenceFlags.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/TemplateBase.h"
@@ -594,6 +595,19 @@ static bool calculateConstraintSatisfaction(
ConstraintSatisfaction &Satisfaction,
UnsignedOrNone PackSubstitutionIndex) {
+ // If the expression has been calculated, e.g. when we are in a nested
+ // requirement, do not compute it repeatedly.
+ if (auto *Expr = Constraint.getConceptSpecializationExpr();
+ Expr && Expr->getDependence() == ExprDependence::None) {
+ auto &Calculated = Expr->getSatisfaction();
+ Satisfaction.ContainsErrors = Calculated.ContainsErrors;
+ Satisfaction.IsSatisfied = Calculated.IsSatisfied;
+ Satisfaction.Details.insert(Satisfaction.Details.end(),
+ Calculated.records().begin(),
+ Calculated.records().end());
+ return !Satisfaction.ContainsErrors;
+ }
+
Sema::ContextRAII CurContext(
S, Constraint.getConceptId()->getNamedConcept()->getDeclContext(),
/*NewThisContext=*/false);
@@ -1598,6 +1612,8 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I
? ArgsAsWritten->arguments()[I].getLocation()
: SourceLocation();
+ // FIXME: Investigate when we couldn't preserve the SourceLoc. What shall we do??
+ // assert(Loc.isValid());
if (OccurringIndices[I]) {
NamedDecl *Param = TemplateParams->begin()[I];
new (&(TempArgs)[J])
@@ -1654,6 +1670,7 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
// If this is an empty pack, we have no corresponding SubstArgs.
if (I < SubstArgs.size())
Loc = SubstArgs.arguments()[I].getLocation();
+ // assert(Loc.isValid());
TempArgs[I] = S.getTrivialTemplateArgumentLoc(CTAI.SugaredConverted[I],
QualType(), Loc);
}
@@ -1688,9 +1705,29 @@ substituteParameterMappings(Sema &S, ConceptIdConstraint &N,
assert(CSE);
TemplateArgumentListInfo Out;
assert(!N.getBeginLoc().isInvalid());
- if (S.SubstTemplateArgumentsInParameterMapping(
- CSE->getTemplateArgsAsWritten()->arguments(), N.getBeginLoc(),
- MLTAL, Out))
+ // TransformTemplateArguments is unable to preserve the source location of a
+ // pack. The SourceLocation is necessary for the instantiation location.
+ // FIXME: The BaseLoc will be used as the location of the pack expansion,
+ // which is wrong.
+ ArgsAsWritten = CSE->getTemplateArgsAsWritten();
+ SourceLocation InstLocBegin =
+ ArgsAsWritten->arguments().empty()
+ ? ArgsAsWritten->getLAngleLoc()
+ : ArgsAsWritten->arguments().front().getSourceRange().getBegin();
+ SourceLocation InstLocEnd =
+ ArgsAsWritten->arguments().empty()
+ ? ArgsAsWritten->getRAngleLoc()
+ : ArgsAsWritten->arguments().front().getSourceRange().getEnd();
+ Sema::InstantiatingTemplate Inst(
+ S, InstLocBegin,
+ Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
+ const_cast<NamedDecl *>(N.getConstraintDecl()),
+ {InstLocBegin, InstLocEnd});
+ if (Inst.isInvalid())
+ return true;
+
+ if (S.SubstTemplateArgumentsInParameterMapping(ArgsAsWritten->arguments(),
+ N.getBeginLoc(), MLTAL, Out))
return true;
Sema::CheckTemplateArgumentInfo CTAI;
if (S.CheckTemplateArgumentList(CSE->getNamedConcept(),
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index f1821b8a420eb..409c0f2d64a38 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3698,10 +3698,6 @@ class TreeTransform {
ParentContext);
}
- /// Build a new Objective-C boxed expression.
- ///
- /// By default, performs semantic analysis to build the new expression.
- /// Subclasses may override this routine to provide different behavior.
ExprResult RebuildConceptSpecializationExpr(NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
>From c996b9e23653b95023fe147441f6a7349160f716 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 23 Jul 2025 21:34:32 +0800
Subject: [PATCH 25/31] nested-requirements.cpp (#52)
* Remove unused headers
* checkpoint
---
clang/include/clang/Sema/Sema.h | 3 +-
clang/lib/Sema/SemaConcept.cpp | 124 ++++++++++--------
clang/lib/Sema/SemaTemplateInstantiate.cpp | 17 ++-
.../expr.prim.req/nested-requirement.cpp | 14 +-
clang/test/SemaCXX/cxx23-assume.cpp | 3 +-
5 files changed, 93 insertions(+), 68 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 59a396616a438..06263a512e506 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14717,7 +14717,8 @@ class Sema final : public SemaBase {
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
- const ConceptReference *TopLevelConceptId = nullptr);
+ const ConceptReference *TopLevelConceptId = nullptr,
+ Expr **ConvertedExpr = nullptr);
/// \brief Check whether the given non-dependent constraint expression is
/// satisfied. Returns false and updates Satisfaction with the satisfaction
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 36d01485ea580..2c8b367df7b20 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -36,8 +36,6 @@
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/Timer.h"
-#include "llvm/Support/WithColor.h"
#include <cstddef>
#include <optional>
@@ -345,7 +343,7 @@ static ExprResult EvaluateAtomicConstraint(
return SubstitutedExpression;
}
-static bool calculateConstraintSatisfaction(
+static ExprResult calculateConstraintSatisfaction(
Sema &S, const NormalizedConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction, UnsignedOrNone PackSubstitutionIndex);
@@ -417,7 +415,7 @@ SubstitutionInTemplateArguments(
return std::move(MLTAL);
}
-static bool calculateConstraintSatisfaction(
+static ExprResult calculateConstraintSatisfaction(
Sema &S, const AtomicConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction,
@@ -497,7 +495,7 @@ static bool calculateConstraintSatisfaction(
if (!Satisfaction.IsSatisfied)
Satisfaction.Details.emplace_back(SubstitutedAtomicExpr.get());
- return SubstitutedAtomicExpr.isUsable();
+ return SubstitutedAtomicExpr;
}
static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
@@ -533,7 +531,7 @@ static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
return NumExpansions;
}
-static bool calculateConstraintSatisfaction(
+static ExprResult calculateConstraintSatisfaction(
Sema &S, const FoldExpandedConstraint &FE, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction) {
@@ -568,12 +566,13 @@ static bool calculateConstraintSatisfaction(
Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
Satisfaction.IsSatisfied = false;
Satisfaction.ContainsErrors = false;
- bool Success = calculateConstraintSatisfaction(
+ // FIXME
+ ExprResult Expr = calculateConstraintSatisfaction(
S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
*SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
// SFINAE errors shouldn't prevent disjunction from evaluating
// FIXME: Does !Success == SFINAE errors occurred?
- if (!Success && Conjunction)
+ if (!Expr.isUsable() && Conjunction)
return false;
if (!Conjunction && Satisfaction.IsSatisfied) {
Satisfaction.Details.erase(Satisfaction.Details.begin() +
@@ -589,25 +588,12 @@ static bool calculateConstraintSatisfaction(
return true;
}
-static bool calculateConstraintSatisfaction(
+static ExprResult calculateConstraintSatisfaction(
Sema &S, const ConceptIdConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction,
UnsignedOrNone PackSubstitutionIndex) {
- // If the expression has been calculated, e.g. when we are in a nested
- // requirement, do not compute it repeatedly.
- if (auto *Expr = Constraint.getConceptSpecializationExpr();
- Expr && Expr->getDependence() == ExprDependence::None) {
- auto &Calculated = Expr->getSatisfaction();
- Satisfaction.ContainsErrors = Calculated.ContainsErrors;
- Satisfaction.IsSatisfied = Calculated.IsSatisfied;
- Satisfaction.Details.insert(Satisfaction.Details.end(),
- Calculated.records().begin(),
- Calculated.records().end());
- return !Satisfaction.ContainsErrors;
- }
-
Sema::ContextRAII CurContext(
S, Constraint.getConceptId()->getNamedConcept()->getDeclContext(),
/*NewThisContext=*/false);
@@ -620,11 +606,12 @@ static bool calculateConstraintSatisfaction(
auto Size = Satisfaction.Details.size();
- bool Ok = calculateConstraintSatisfaction(
+ ExprResult E = calculateConstraintSatisfaction(
S, Constraint.getNormalizedConstraint(), Template, TemplateNameLoc, MLTAL,
Satisfaction, PackSubstitutionIndex);
- if (Size != Satisfaction.Details.size()) {
+ if (!E.isUsable())
+ return E;
llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
@@ -633,7 +620,7 @@ static bool calculateConstraintSatisfaction(
PackSubstitutionIndex);
if (!SubstitutedArgs)
- return Ok;
+ return E.isUsable();
Sema::SFINAETrap Trap(S);
Sema::ArgPackSubstIndexRAII SubstIndex(
@@ -652,12 +639,15 @@ static bool calculateConstraintSatisfaction(
AdjustConstraintDepth Adjust(S, Depth);
if (Adjust.TransformTemplateArguments(Ori->getTemplateArgs(),
Ori->NumTemplateArgs, TransArgs))
- return Ok;
+ return false;
if (S.SubstTemplateArguments(TransArgs.arguments(), *SubstitutedArgs,
OutArgs) ||
- Trap.hasErrorOccurred())
- return Ok;
+ Trap.hasErrorOccurred()) {
+ Satisfaction.ContainsErrors = true;
+ Satisfaction.IsSatisfied = false;
+ return false;
+ }
CXXScopeSpec SS;
SS.Adopt(Constraint.getConceptId()->getNestedNameSpecifierLoc());
@@ -669,7 +659,9 @@ static bool calculateConstraintSatisfaction(
/*CheckConstraintSatisfaction=*/false);
if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
- return Ok;
+ return false;
+
+ if (Size != Satisfaction.Details.size()) {
Satisfaction.Details.insert(
Satisfaction.Details.begin() + Size,
@@ -677,10 +669,10 @@ static bool calculateConstraintSatisfaction(
SubstitutedConceptId.getAs<ConceptSpecializationExpr>()
->getConceptReference()));
}
- return Ok;
+ return SubstitutedConceptId;
}
-static bool calculateConstraintSatisfaction(
+static ExprResult calculateConstraintSatisfaction(
Sema &S, const CompoundConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction,
@@ -691,35 +683,49 @@ static bool calculateConstraintSatisfaction(
bool Conjunction =
Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction;
- bool Ok = calculateConstraintSatisfaction(
+ ExprResult LHS = calculateConstraintSatisfaction(
S, Constraint.getLHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
- if (Conjunction && !Ok)
+ if (Conjunction && !LHS.isUsable())
return false;
- if (!Conjunction && Ok && Satisfaction.IsSatisfied &&
+ if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied &&
!Satisfaction.ContainsErrors)
- return true;
+ return LHS;
- if (Conjunction && Ok &&
+ if (Conjunction && LHS.isUsable() &&
(!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
- return true;
+ return LHS;
Satisfaction.ContainsErrors = false;
Satisfaction.IsSatisfied = false;
- Ok = calculateConstraintSatisfaction(S, Constraint.getRHS(), Template,
- TemplateNameLoc, MLTAL, Satisfaction,
+ ExprResult RHS = calculateConstraintSatisfaction(
+ S, Constraint.getRHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
- if (Ok && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
+ if (RHS.isUsable() && Satisfaction.IsSatisfied && !Satisfaction.ContainsErrors)
Satisfaction.Details.erase(Satisfaction.Details.begin() +
EffectiveDetailEndIndex,
Satisfaction.Details.end());
- return Ok;
+
+ if (!LHS.isUsable())
+ return RHS;
+
+ if (!RHS.isUsable())
+ return LHS;
+
+ return BinaryOperator::Create(
+ S.Context, LHS.get(), RHS.get(),
+ BinaryOperator::getOverloadedOpcode(
+ Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction
+ ? OO_AmpAmp
+ : OO_PipePipe),
+ S.Context.BoolTy, VK_PRValue, OK_Ordinary, Constraint.getBeginLoc(),
+ FPOptionsOverride{});
}
-static bool calculateConstraintSatisfaction(
+static ExprResult calculateConstraintSatisfaction(
Sema &S, const NormalizedConstraint &Constraint, const NamedDecl *Template,
SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
ConstraintSatisfaction &Satisfaction,
@@ -753,7 +759,10 @@ static bool CheckConstraintSatisfaction(
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
- const ConceptReference *TopLevelConceptId = nullptr) {
+ Expr **ConvertedExpr, const ConceptReference *TopLevelConceptId = nullptr) {
+
+ if (ConvertedExpr)
+ *ConvertedExpr = nullptr;
if (AssociatedConstraints.empty()) {
Satisfaction.IsSatisfied = true;
@@ -790,7 +799,6 @@ static bool CheckConstraintSatisfaction(
const_cast<NormalizedConstraint *>(C),
Template, /*CSE=*/nullptr,
S.ArgPackSubstIndex);
- }
ExprResult Res = calculateConstraintSatisfaction(
S, *C, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
@@ -810,16 +818,16 @@ bool Sema::CheckConstraintSatisfaction(
ArrayRef<AssociatedConstraint> AssociatedConstraints,
const MultiLevelTemplateArgumentList &TemplateArgsLists,
SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction,
- const ConceptReference *TopLevelConceptId) {
+ const ConceptReference *TopLevelConceptId, Expr **ConvertedExpr) {
if (AssociatedConstraints.empty()) {
OutSatisfaction.IsSatisfied = true;
return false;
}
const auto *Template = Entity.dyn_cast<const NamedDecl *>();
if (!Template) {
- return ::CheckConstraintSatisfaction(*this, nullptr, AssociatedConstraints,
- TemplateArgsLists, TemplateIDRange,
- OutSatisfaction, TopLevelConceptId);
+ return ::CheckConstraintSatisfaction(
+ *this, nullptr, AssociatedConstraints, TemplateArgsLists,
+ TemplateIDRange, OutSatisfaction, ConvertedExpr, TopLevelConceptId);
}
// Invalid templates could make their way here. Substituting them could result
// in dependent expressions.
@@ -850,13 +858,15 @@ bool Sema::CheckConstraintSatisfaction(
auto Satisfaction =
std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs);
- if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints,
- TemplateArgsLists, TemplateIDRange,
- *Satisfaction, TopLevelConceptId)) {
+ if (::CheckConstraintSatisfaction(
+ *this, Template, AssociatedConstraints, TemplateArgsLists,
+ TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) {
OutSatisfaction = *Satisfaction;
return true;
}
+ // FIXME: cache ConvertedExpr for nested requirements?
+
if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
// The evaluation of this constraint resulted in us trying to re-evaluate it
// recursively. This isn't really possible, except we try to form a
@@ -1612,7 +1622,8 @@ substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I
? ArgsAsWritten->arguments()[I].getLocation()
: SourceLocation();
- // FIXME: Investigate when we couldn't preserve the SourceLoc. What shall we do??
+ // FIXME: Investigate when we couldn't preserve the SourceLoc. What shall
+ // we do??
// assert(Loc.isValid());
if (OccurringIndices[I]) {
NamedDecl *Param = TemplateParams->begin()[I];
@@ -1945,9 +1956,14 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr(
const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq,
ArrayRef<AssociatedConstraint> AssociatedConstraints) {
- if (!ConstrainedDeclOrNestedReq)
- return NormalizedConstraint::fromAssociatedConstraints(
+ if (!ConstrainedDeclOrNestedReq) {
+ auto *Normalized = NormalizedConstraint::fromAssociatedConstraints(
*this, nullptr, AssociatedConstraints);
+ if (!Normalized || substituteParameterMappings(*this, *Normalized))
+ return nullptr;
+
+ return Normalized;
+ }
// FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement!
const NamedDecl *ND =
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 7bdfb4014b965..02d8963885547 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2939,6 +2939,7 @@ TemplateInstantiator::TransformNestedRequirement(
return Req;
}
+#if 0
if (Req->isDependent() || AlwaysRebuild()) {
Sema::InstantiatingTemplate ReqInst(
SemaRef, Constraint->getBeginLoc(), Req,
@@ -2956,16 +2957,20 @@ TemplateInstantiator::TransformNestedRequirement(
if (Constraint->isInstantiationDependent())
return new (C) concepts::NestedRequirement(Constraint);
+#endif
bool Success;
+ Expr *NewConstraint;
TemplateDeductionInfo Info(Constraint->getBeginLoc());
{
EnterExpressionEvaluationContext ContextRAII(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
- Sema::InstantiatingTemplate ConstrInst(SemaRef, Constraint->getBeginLoc(),
- Req, Info,
+ Sema::InstantiatingTemplate ConstrInst(
+ SemaRef, Constraint->getBeginLoc(), Req,
+ Sema::InstantiatingTemplate::ConstraintsCheck(),
Constraint->getSourceRange());
+
if (ConstrInst.isInvalid())
return nullptr;
@@ -2973,7 +2978,8 @@ TemplateInstantiator::TransformNestedRequirement(
Success = !SemaRef.CheckConstraintSatisfaction(
Req, AssociatedConstraint(Constraint, SemaRef.ArgPackSubstIndex),
- TemplateArgs, Constraint->getSourceRange(), Satisfaction);
+ TemplateArgs, Constraint->getSourceRange(), Satisfaction,
+ /*TopLevelConceptId=*/nullptr, &NewConstraint);
assert(!Success || !Trap.hasErrorOccurred() &&
"Substitution failures must be handled "
@@ -2984,7 +2990,10 @@ TemplateInstantiator::TransformNestedRequirement(
return NestedReqWithDiag(Constraint, Satisfaction);
// FIXME: const correctness
- return new (C) concepts::NestedRequirement(C, Constraint, Satisfaction);
+ // MLTAL might be dependent.
+ if (!NewConstraint)
+ NewConstraint = Constraint;
+ return new (C) concepts::NestedRequirement(C, NewConstraint, Satisfaction);
}
TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
index 033ae349a02e5..48763319a4598 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
@@ -43,11 +43,10 @@ namespace std_example {
requires sizeof(a) == 4; // OK
requires a == 0; // expected-error{{substitution into constraint expression resulted in a non-constant expression}}
// expected-note at -1{{while checking the satisfaction of nested requirement requested here}}
- // expected-note at -2{{in instantiation of requirement here}}
- // expected-note at -3{{while checking the satisfaction of nested requirement requested here}}
- // expected-note at -6{{while substituting template arguments into constraint expression here}}
- // expected-note at -5{{function parameter 'a' with unknown value cannot be used in a constant expression}}
- // expected-note at -8{{declared here}}
+ // expected-note at -2{{while checking the satisfaction of nested requirement requested here}}
+ // expected-note at -5{{while substituting template arguments into constraint expression here}}
+ // expected-note at -4{{function parameter 'a' with unknown value cannot be used in a constant expression}}
+ // expected-note at -7{{declared here}}
};
static_assert(C2<int>); // expected-error{{static assertion failed}}
// expected-note at -1{{while checking the satisfaction of concept 'C2<int>' requested here}}
@@ -161,12 +160,13 @@ void func() {
// expected-note@#bar {{because 'X<SubstitutionFailureNestedRequires::ErrorExpressions_NotSF::False>::value' evaluated to false}}
bar<int>();
+ // expected-error at -1 {{no matching function for call to 'bar'}} \
// expected-note at -1 {{while checking constraint satisfaction for template 'bar<int>' required here}} \
- // expected-note at -1 {{while substituting deduced template arguments into function template 'bar' [with T = int]}}
+ // expected-note at -1 {{while substituting deduced template arguments into function template 'bar' [with T = int]}} \
// expected-note@#bar {{in instantiation of static data member}}
- // expected-note@#bar {{in instantiation of requirement here}}
// expected-note@#bar {{while checking the satisfaction of nested requirement requested here}}
// expected-note@#bar {{while substituting template arguments into constraint expression here}}
+ // expected-note@#bar {{candidate template ignored}}
// expected-error@#X_Value {{type 'int' cannot be used prior to '::' because it has no members}}
}
}
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index d018368cb96c4..ce862666aa48f 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -132,8 +132,7 @@ constexpr int f5() requires C<T> { return 1; } // expected-note {{while checking
template <typename T>
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}}
+ // expected-note {{candidate template ignored}}
static_assert(f5<int>() == 1);
static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint satisfaction}}
>From cc13e879a0edac029c8cfaefd9a1bd01b3bfe23b Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sun, 10 Aug 2025 19:20:54 +0800
Subject: [PATCH 26/31] Fix more tests in error handling (#53)
* Checkpoint
Failed Tests (12):
Clang :: CXX/drs/cwg25xx.cpp
Clang :: CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
Clang :: CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
Clang :: CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp
Clang :: CXX/temp/temp.constr/temp.constr.normal/p1.cpp
Clang :: SemaCXX/concept-crash-on-diagnostic.cpp
Clang :: SemaCXX/cxx23-assume.cpp
Clang :: SemaCXX/cxx2c-fold-exprs.cpp
Clang :: SemaCXX/invalid-requirement-requires-expr.cpp
Clang :: SemaTemplate/concepts-recursive-inst.cpp
Clang :: SemaTemplate/concepts.cpp
Clang :: SemaTemplate/cxx2a-constraint-exprs.cpp
* Fix more tests in error handling
Remaining Failed Tests (7):
Clang :: AST/ByteCode/libcxx/primitive-temporary.cpp
Clang :: CXX/drs/cwg25xx.cpp
Clang :: CXX/temp/temp.constr/temp.constr.normal/p1.cpp
Clang :: SemaCXX/cxx2c-fold-exprs.cpp
Clang :: SemaTemplate/alias-template-with-lambdas.cpp
Clang :: SemaTemplate/concepts-recursive-inst.cpp
Clang :: SemaTemplate/instantiate-abbreviated-template.cpp
* Fix one more test
* save
* Fix more tests
* Do not set ContainsError on substitution failure
That would completely break overload resolution;
see https://github.com/llvm/llvm-project/commit/684a78968bd32b0ece5b852bcaa21dbc49a4b5b0#diff-b7090bd1c9146da9ed3ff99bef9fa52903cf7034e9bca340446ffa0ab3549d04
```cpp
template <class _Tp, class _Up>
concept same_as = __is_same(_Up, _Tp);
template <class> using type_identity_t = int;
enum __arg_t {};
template <class _Context, same_as<typename _Context::char_type>>
void __determine_arg_t();
template <class, class> __arg_t __determine_arg_t();
struct array {
int data;
};
struct __compile_time_basic_format_context;
template <class, class... _Args> struct basic_format_string {
template <class _Tp> basic_format_string(_Tp __str) {}
array __types_{
__determine_arg_t<__compile_time_basic_format_context, _Args>()...};
};
template <class... _Args>
using format_string = basic_format_string<char, type_identity_t<_Args>...>;
template <class... _Args> void format(format_string<_Args...>, _Args...);
void __format() {
format("", char());
}
```
* Reapply 748371183ae: Instantiate concepts with sugared template arguments
The sugared arguments of concepts are necessary to compile the following case,
thanks to a Profile bug of DependentDecltypeType:
namespace {
template <int __v> struct integral_constant {
static const int value = __v;
};
template <class _Tp> _Tp forward;
struct _IfImpl {
template <class _IfRes, class> using _Select = _IfRes;
};
template <bool, class _IfRes, class _ElseRes>
using _If = _IfImpl::_Select<_IfRes, _ElseRes>;
template <class _If> struct conditional {
using type = _If;
};
template <bool, class _If, class>
using __conditional_t = conditional<_If>::type;
template <class _Tp> struct enable_if {
typedef _Tp type;
};
template <class _Tp> _Tp __declval(long);
template <class _Tp> decltype(__declval<_Tp>(0)) declval();
template <class _Fp, class... _Args>
decltype(_Fp()(declval<_Args>()...)) __invoke(_Fp, _Args...);
template <class, class _Fp, class... _Args> struct __invokable_r {
template <class _XFp, class... _XArgs>
static decltype(__invoke(_XFp(), declval<_XArgs>()...)) __try_call(int);
using _Result = decltype(__try_call<_Fp, _Args...>(0));
};
template <class _Func, class... _Args>
struct __invoke_result
: enable_if<typename __invokable_r<void, _Func, _Args...>::_Result> {};
template <class _Fn, class... _Args>
using invoke_result_t = __invoke_result<_Fn, _Args...>::type;
template <class _From, class _To>
constexpr bool is_convertible_v = __is_convertible(_From, _To);
template <class _From, class>
concept convertible_to = requires { _From(); };
template <class _Tp>
concept move_constructible = convertible_to<_Tp, _Tp>;
template <class _Tp> constexpr bool is_object_v = __is_object(_Tp);
template <class _Dp, class _Bp>
concept derived_from = is_convertible_v<_Dp, _Bp>;
template <class _Tp>
concept __boolean_testable = requires { forward<_Tp>; };
template <class _Fn, class... _Args>
invoke_result_t<_Fn, _Args...> invoke(_Fn, _Args...);
template <class _Fn, class... _Args>
concept invocable =
requires(_Fn __fn, _Args... __args) { invoke(__fn, __args...); };
template <class _Fn, class... _Args>
concept regular_invocable = invocable<_Fn, _Args...>;
template <class _Fn, class... _Args>
concept predicate = __boolean_testable<invoke_result_t<_Fn, _Args...>>;
template <class>
using iter_difference_t =
decltype(static_cast<int *>(nullptr) - static_cast<int *>(nullptr));
template <decltype(sizeof(int)), class> struct tuple_element;
template <class...> struct tuple {};
template <decltype(sizeof(int)) _Ip, class... _Tp>
tuple_element<_Ip, tuple<_Tp...>>::type get(tuple<_Tp...>);
template <class _Tp, _Tp...> struct integer_sequence;
template <decltype(sizeof(int))... _Ip>
using index_sequence = integer_sequence<decltype(sizeof(int)), _Ip...>;
template <class _Tp, _Tp _Ep>
using make_integer_sequence = __make_integer_seq<integer_sequence, _Tp, _Ep>;
template <decltype(sizeof(int)) _Np>
using make_index_sequence = make_integer_sequence<decltype(sizeof(int)), _Np>;
template <class... _Tp>
using index_sequence_for = make_index_sequence<sizeof...(_Tp)>;
template <class...> struct __tuple_types;
template <decltype(sizeof(int)) _Ip, class... _Types>
struct tuple_element<_Ip, __tuple_types<_Types...>> {
using type = __type_pack_element<_Ip, _Types...>;
};
template <class _Tp>
concept __dereferenceable = requires(_Tp __t) { __t; };
template <__dereferenceable _Tp> using iter_reference_t = decltype(*_Tp());
struct input_iterator_tag {};
struct forward_iterator_tag : input_iterator_tag {};
struct bidirectional_iterator_tag : forward_iterator_tag {};
struct contiguous_iterator_tag : bidirectional_iterator_tag {};
auto to_address(0);
template <class _Ip>
concept input_or_output_iterator = requires(_Ip __i) { __i; };
template <class _Sp, class _Ip>
concept sized_sentinel_for = requires(_Ip __i, _Sp __s) { __i - __s; };
template <class _Ip>
concept input_iterator = derived_from<typename _Ip::iterator_concept, input_iterator_tag>;
template <class _Ip>
concept forward_iterator =
derived_from<typename _Ip::iterator_concept, forward_iterator_tag>;
template <class>
concept bidirectional_iterator = requires { to_address; };
template <class _Fp, class _It>
concept indirect_unary_predicate = predicate<_Fp, iter_reference_t<_It>>;
namespace ranges {
struct {
template <class _Tp> auto operator()(_Tp __t) { return __t.begin(); }
} begin;
template <class _Tp> using iterator_t = decltype(begin(declval<_Tp>()));
template <class> constexpr bool enable_view = requires { nullptr; };
template <class _Tp>
concept __difference = requires(_Tp __t) {
{ begin(__t) } -> sized_sentinel_for<decltype(begin(declval<_Tp>()))>;
// { begin(__t) } -> sized_sentinel_for<decltype(begin(_Tp()))>;
};
struct {
template <__difference _Tp> auto operator()(_Tp __t) {
auto __trans_tmp_1(__t);
0 - __trans_tmp_1;
}
} size;
template <class _Tp>
concept range = requires(_Tp __t) { __t; };
template <class _Tp>
concept input_range = input_iterator<iterator_t<_Tp>>;
template <range _Rp> using range_difference_t = iter_difference_t<_Rp>;
template <range _Rp>
using range_reference_t = iter_reference_t<iterator_t<_Rp>>;
template <class _Tp>
concept sized_range = requires(_Tp __t) { size(__t); };
template <class _Tp>
concept forward_range = forward_iterator<iterator_t<_Tp>>;
template <class _Tp>
concept bidirectional_range = bidirectional_iterator<_Tp>;
} // namespace ranges
template <class> struct tuple_size;
template <class... _Tp>
struct tuple_size<tuple<_Tp...>> : integral_constant<sizeof...(_Tp)> {};
template <class _Tp>
constexpr decltype(sizeof(int)) tuple_size_v = tuple_size<_Tp>::value;
namespace ranges {
template <class _Derived> struct view_interface {
template <class _D2 = _Derived>
void front()
requires forward_range<_D2>;
};
} // namespace ranges
struct __rule {};
struct basic_string {};
template <decltype(sizeof(int)) _Ip, class... _Tp>
struct tuple_element<_Ip, tuple<_Tp...>> {
using type = tuple_element<_Ip, __tuple_types<_Tp...>>::type;
};
template <bool _Const, class _Tp>
using __maybe_const = __conditional_t<_Const, _Tp, _Tp>;
template <class...> struct __perfect_forward_impl;
template <class _Op, decltype(sizeof(int))... _Idx, class... _BoundArgs>
struct __perfect_forward_impl<_Op, index_sequence<_Idx...>, _BoundArgs...> {
tuple<_BoundArgs...> __bound_args_;
template <class... _Args>
auto operator()(_Args... __args)
-> decltype(_Op()(get<_Idx>(__bound_args_)..., __args...));
};
template <class _Op, class... _Args>
using __perfect_forward =
__perfect_forward_impl<_Op, index_sequence_for<_Args...>, _Args...>;
struct __wrap_iter {
typedef contiguous_iterator_tag iterator_concept;
__rule operator*();
};
struct vector {
__wrap_iter begin();
};
namespace ranges {
template <class>
concept _RangeAdaptorClosure = requires { nullptr; };
template <range _Range, _RangeAdaptorClosure _Closure>
auto operator|(_Range __range, _Closure __closure) {
return invoke(__closure, __range);
}
} // namespace ranges
template <input_or_output_iterator _Iter> struct counted_iterator : _Iter {
counted_iterator(_Iter, iter_difference_t<_Iter>);
};
template <decltype(sizeof(int)) _NBound, class = make_index_sequence<_NBound>>
struct __bind_back_op;
template <decltype(sizeof(int)) _NBound, decltype(sizeof(int))... _Ip>
struct __bind_back_op<_NBound, index_sequence<_Ip...>> {
template <class _Fn, class _BoundArgs, class... _Args>
auto operator()(_Fn __f, _BoundArgs __bound_args, _Args... __args)
-> decltype(invoke(__f, __args..., get<_Ip>(__bound_args)...));
};
template <class _Fn, class _BoundArgs>
struct __bind_back_t
: __perfect_forward<__bind_back_op<tuple_size_v<_BoundArgs>>, _Fn,
_BoundArgs> {};
template <class _Fn, class... _Args>
auto __bind_back(_Fn, _Args...)
-> decltype(__bind_back_t<_Fn, tuple<_Args...>>());
namespace ranges {
struct __empty_cache;
template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
requires is_object_v<_Pred>
class filter_view {
class __iterator;
public:
filter_view(_View, _Pred);
__iterator begin();
};
template <input_range _View, indirect_unary_predicate<iterator_t<_View>> _Pred>
requires is_object_v<_Pred>
class filter_view<_View, _Pred>::__iterator {
public:
using iterator_concept =
_If<bidirectional_range<_View>, bidirectional_iterator_tag,
_If<forward_range<_View>, forward_iterator_tag, input_iterator_tag>>;
range_reference_t<_View> operator*();
};
struct {
template <class _Range, class _Pred>
auto operator()(_Range __range, _Pred __pred)
-> decltype(filter_view(__range, __pred));
template <class _Pred> auto operator()(_Pred __pred) {
return __bind_back(*this, __pred);
}
} filter;
template <input_range _View> struct lazy_split_view {
template <int _Const> struct __outer_iterator {
using _Base = __maybe_const<_Const, _View>;
_If<forward_range<_View>, iterator_t<_Base>, __empty_cache>;
};
};
template <class _View> struct take_view : view_interface<take_view<_View>> {
_View __base_;
range_difference_t<_View> __count_;
take_view(_View, range_difference_t<_View>);
auto begin() {
sized_range<_View>;
return counted_iterator(ranges::begin(__base_), __count_);
}
};
struct {
template <class _Range, convertible_to<_Range> _Np>
auto operator()(_Range __range, _Np __n) -> decltype(take_view(__range, __n));
auto operator()(int __n) { return __bind_back(*this, __n); }
} take;
template <class _View, class _Fn>
concept __transform_view_constraints =
regular_invocable<_Fn, invoke_result_t<_Fn, range_reference_t<_View>>>;
template <class _View, class _Fn>
struct transform_view {
template <bool> class __iterator;
transform_view(_View, _Fn);
__iterator<false> begin();
};
template <class> struct __transform_view_iterator_concept {
using type = forward_iterator_tag;
};
template <class _View, class _Fn>
template <bool _Const>
class transform_view<_View, _Fn>::__iterator {
using _Base = __maybe_const<_Const, _View>;
public:
using iterator_concept = __transform_view_iterator_concept<_View>::type;
_Base friend operator-(__iterator, __iterator)
// iterator_t<_Base> was canonicalized to the type of the one written on line 221 iterator_t<_Base>.
requires sized_sentinel_for<_Base, iterator_t<_Base>>
{}
};
struct {
template <class _Range, class _Fn>
auto operator()(_Range __range, _Fn __f)
-> decltype(transform_view(__range, __f));
template <class _Fn> auto operator()(_Fn __f) {
return __bind_back(*this, __f);
}
} transform;
} // namespace ranges
namespace views = ranges;
vector __letters_before_first_rule___rules;
basic_string __letters_before_first_rule() {
auto __letters = __letters_before_first_rule___rules |
views::filter([](__rule) { return 0; }) |
views::transform([](__rule __rule) { return __rule; }) |
views::take(1);
__letters.front();
}
} // namespace
---
clang/docs/ReleaseNotes.rst | 91 +++++++++
clang/include/clang/Sema/Template.h | 5 +-
clang/lib/Sema/SemaConcept.cpp | 188 +++++++++++-------
clang/lib/Sema/SemaExprCXX.cpp | 5 +-
clang/lib/Sema/SemaTemplate.cpp | 20 +-
clang/lib/Sema/SemaTemplateDeduction.cpp | 17 +-
clang/lib/Serialization/ASTReaderDecl.cpp | 2 +-
clang/test/AST/ast-dump-concepts.cpp | 10 +-
clang/test/AST/ast-dump-ctad-alias.cpp | 22 +-
clang/test/CXX/drs/cwg25xx.cpp | 4 +-
.../expr.prim.req/compound-requirement.cpp | 10 +-
.../expr.prim.req/nested-requirement.cpp | 2 +-
.../expr.prim.req/simple-requirement.cpp | 4 +-
.../expr.prim.req/type-requirement.cpp | 12 +-
.../constrant-satisfaction-conversions.cpp | 2 +-
clang/test/CXX/temp/temp.param/p10-2a.cpp | 23 ++-
clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 63 ++++--
.../invalid-requirement-requires-expr.cpp | 4 +-
clang/test/SemaCXX/type-traits.cpp | 4 +-
clang/test/SemaHLSL/BuiltIns/Buffers.hlsl | 6 +-
clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl | 6 +-
.../SemaTemplate/concepts-recursive-inst.cpp | 27 +--
clang/test/SemaTemplate/deduction-guide.cpp | 15 +-
.../instantiate-abbreviated-template.cpp | 1 -
.../instantiate-requires-expr.cpp | 20 +-
clang/test/SemaTemplate/pr52970.cpp | 2 +-
26 files changed, 379 insertions(+), 186 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a8b7a29933945..3d061cc6a6a35 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -181,6 +181,7 @@ Bug Fixes to Attribute Support
Bug Fixes to C++ Support
^^^^^^^^^^^^^^^^^^^^^^^^
+<<<<<<< HEAD
- Diagnose binding a reference to ``*nullptr`` during constant evaluation. (#GH48665)
- Suppress ``-Wdeprecated-declarations`` in implicitly generated functions. (#GH147293)
- Fix a crash when deleting a pointer to an incomplete array (#GH150359).
@@ -189,6 +190,96 @@ Bug Fixes to C++ Support
- Fix the dynamic_cast to final class optimization to correctly handle
casts that are guaranteed to fail (#GH137518).
- Fix bug rejecting partial specialization of variable templates with auto NTTPs (#GH118190).
+=======
+
+- Clang now supports implicitly defined comparison operators for friend declarations. (#GH132249)
+- Clang now diagnoses copy constructors taking the class by value in template instantiations. (#GH130866)
+- Clang is now better at keeping track of friend function template instance contexts. (#GH55509)
+- Clang now prints the correct instantiation context for diagnostics suppressed
+ by template argument deduction.
+- Errors that occur during evaluation of certain type traits and builtins are
+ no longer incorrectly emitted when they are used in an SFINAE context. The
+ type traits are:
+
+ - ``__is_constructible`` and variants,
+ - ``__is_convertible`` and variants,
+ - ``__is_assignable`` and variants,
+ - ``__reference_binds_to_temporary``,
+ ``__reference_constructs_from_temporary``,
+ ``__reference_converts_from_temporary``,
+ - ``__is_trivially_equality_comparable``.
+
+ The builtin is ``__builtin_common_type``. (#GH132044)
+- Clang is now better at instantiating the function definition after its use inside
+ of a constexpr lambda. (#GH125747)
+- Fixed a local class member function instantiation bug inside dependent lambdas. (#GH59734), (#GH132208)
+- Clang no longer crashes when trying to unify the types of arrays with
+ certain differences in qualifiers (this could happen during template argument
+ deduction or when building a ternary operator). (#GH97005)
+- Fixed type alias CTAD issues involving default template arguments. (#GH134471)
+- Fixed CTAD issues when initializing anonymous fields with designated initializers. (#GH67173)
+- The initialization kind of elements of structured bindings
+ direct-list-initialized from an array is corrected to direct-initialization.
+- Clang no longer crashes when a coroutine is declared ``[[noreturn]]``. (#GH127327)
+- Clang now uses the parameter location for abbreviated function templates in ``extern "C"``. (#GH46386)
+- Clang will emit an error instead of crash when use co_await or co_yield in
+ C++26 braced-init-list template parameter initialization. (#GH78426)
+- Improved fix for an issue with pack expansions of type constraints, where this
+ now also works if the constraint has non-type or template template parameters.
+ (#GH131798)
+- Fixes to partial ordering of non-type template parameter packs. (#GH132562)
+- Fix crash when evaluating the trailing requires clause of generic lambdas which are part of
+ a pack expansion.
+- Fixes matching of nested template template parameters. (#GH130362)
+- Correctly diagnoses template template parameters which have a pack parameter
+ not in the last position.
+- Disallow overloading on struct vs class on dependent types, which is IFNDR, as
+ this makes the problem diagnosable.
+- Improved preservation of the presence or absence of typename specifier when
+ printing types in diagnostics.
+- Clang now correctly parses ``if constexpr`` expressions in immediate function context. (#GH123524)
+- Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272)
+- Clang now properly instantiates destructors for initialized members within non-delegating constructors. (#GH93251)
+- Correctly diagnoses if unresolved using declarations shadows template parameters (#GH129411)
+- Fixed C++20 aggregate initialization rules being incorrectly applied in certain contexts. (#GH131320)
+- Clang was previously coalescing volatile writes to members of volatile base class subobjects.
+ The issue has been addressed by propagating qualifiers during derived-to-base conversions in the AST. (#GH127824)
+- Correctly propagates the instantiated array type to the ``DeclRefExpr`` that refers to it. (#GH79750), (#GH113936), (#GH133047)
+- Fixed a Clang regression in C++20 mode where unresolved dependent call expressions were created inside non-dependent contexts (#GH122892)
+- Clang now emits the ``-Wunused-variable`` warning when some structured bindings are unused
+ and the ``[[maybe_unused]]`` attribute is not applied. (#GH125810)
+- Declarations using class template argument deduction with redundant
+ parentheses around the declarator are no longer rejected. (#GH39811)
+- Fixed a crash caused by invalid declarations of ``std::initializer_list``. (#GH132256)
+- Clang no longer crashes when establishing subsumption between some constraint expressions. (#GH122581)
+- Clang now issues an error when placement new is used to modify a const-qualified variable
+ in a ``constexpr`` function. (#GH131432)
+- Fixed an incorrect TreeTransform for calls to ``consteval`` functions if a conversion template is present. (#GH137885)
+- Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806)
+- Fixed a missed initializer instantiation bug for variable templates. (#GH134526), (#GH138122)
+- Fix a crash when checking the template template parameters of a dependent lambda appearing in an alias declaration.
+ (#GH136432), (#GH137014), (#GH138018)
+- Fixed an assertion when trying to constant-fold various builtins when the argument
+ referred to a reference to an incomplete type. (#GH129397)
+- Fixed a crash when a cast involved a parenthesized aggregate initialization in dependent context. (#GH72880)
+- No longer crashes when instantiating invalid variable template specialization
+ whose type depends on itself. (#GH51347), (#GH55872)
+- Improved parser recovery of invalid requirement expressions. In turn, this
+ fixes crashes from follow-on processing of the invalid requirement. (#GH138820)
+- Fixed the handling of pack indexing types in the constraints of a member function redeclaration. (#GH138255)
+- Clang now correctly parses arbitrary order of ``[[]]``, ``__attribute__`` and ``alignas`` attributes for declarations (#GH133107)
+- Fixed a crash when forming an invalid function type in a dependent context. (#GH138657) (#GH115725) (#GH68852)
+- Clang no longer segfaults when there is a configuration mismatch between modules and their users (http://crbug.com/400353616).
+- Fix an incorrect deduction when calling an explicit object member function template through an overload set address.
+- Fixed bug in constant evaluation that would allow using the value of a
+ reference in its own initializer in C++23 mode (#GH131330).
+- Clang could incorrectly instantiate functions in discarded contexts (#GH140449)
+- Fix instantiation of default-initialized variable template specialization. (#GH140632) (#GH140622)
+- Clang modules now allow a module and its user to differ on TrivialAutoVarInit*
+- Fixed an access checking bug when initializing non-aggregates in default arguments (#GH62444), (#GH83608)
+- Fixed a pack substitution bug in deducing class template partial specializations. (#GH53609)
+- Fixed a crash when constant evaluating some explicit object member assignment operators. (#GH142835)
+>>>>>>> 14b0e97a546e (Fix more tests in error handling (#53))
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 5160fc0b820f5..bfcd070074d56 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -234,7 +234,8 @@ enum class TemplateSubstitutionKind : char {
/// Replaces the current 'innermost' level with the provided argument list.
/// This is useful for type deduction cases where we need to get the entire
/// list from the AST, but then add the deduced innermost list.
- void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) {
+ void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args,
+ bool Final = false) {
assert((!TemplateArgumentLists.empty() || NumRetainedOuterLevels) &&
"Replacing in an empty list?");
@@ -249,7 +250,7 @@ enum class TemplateSubstitutionKind : char {
} else {
--NumRetainedOuterLevels;
TemplateArgumentLists.push_back(
- {{AssociatedDecl, /*Final=*/false}, Args});
+ {{AssociatedDecl, /*Final=*/Final}, Args});
}
}
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 2c8b367df7b20..fa649a0142645 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -193,7 +193,8 @@ static bool DiagRecursiveConstraintEval(
if (MLTAL) {
for (const auto &List : *MLTAL)
for (const auto &TemplateArg : List.Args)
- TemplateArg.Profile(ID, S.Context);
+ S.Context.getCanonicalTemplateArgument(TemplateArg)
+ .Profile(ID, S.Context);
}
if (S.SatisfactionStackContains(Templ, ID)) {
S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self)
@@ -353,11 +354,15 @@ SubstitutionInTemplateArguments(
Sema &S, const NormalizedConstraintWithParamMapping &Constraint,
const NamedDecl *Template, MultiLevelTemplateArgumentList MLTAL,
llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost,
+ ConstraintSatisfaction &Satisfaction,
// FIXME: Having both PackSubstitutionIndex and
// NormalizedConstraintWithParamMapping::getPackSubstitutionIndex is
// confusing
UnsignedOrNone PackSubstitutionIndex) {
+ if (!Constraint.hasParameterMapping())
+ return std::move(MLTAL);
+
Sema::InstantiatingTemplate Inst(
S, Constraint.getBeginLoc(),
Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
@@ -369,16 +374,18 @@ SubstitutionInTemplateArguments(
Sema::SFINAETrap Trap(S);
TemplateArgumentListInfo SubstArgs;
- if (Constraint.hasParameterMapping()) {
Sema::ArgPackSubstIndexRAII SubstIndex(
S, Constraint.getPackSubstitutionIndex()
? Constraint.getPackSubstitutionIndex()
: PackSubstitutionIndex);
+
if (S.SubstTemplateArgumentsInParameterMapping(
Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL,
- SubstArgs) ||
- Trap.hasErrorOccurred())
+ SubstArgs)) {
+ Satisfaction.IsSatisfied = false;
return std::nullopt;
+ }
+
Sema::CheckTemplateArgumentInfo CTAI;
auto *TD = const_cast<TemplateDecl *>(
cast<TemplateDecl>(Constraint.getConstraintDecl()));
@@ -387,8 +394,7 @@ SubstitutionInTemplateArguments(
/*DefaultArguments=*/{},
/*PartialTemplateArgs=*/false, CTAI))
return std::nullopt;
- NormalizedConstraint::OccurenceList Used =
- Constraint.mappingOccurenceList();
+ NormalizedConstraint::OccurenceList Used = Constraint.mappingOccurenceList();
SubstitutedOuterMost =
llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
unsigned Offset = 0;
@@ -411,7 +417,6 @@ SubstitutionInTemplateArguments(
MLTAL.replaceOutermostTemplateArguments(
const_cast<NamedDecl *>(Constraint.getConstraintDecl()),
SubstitutedOuterMost);
- }
return std::move(MLTAL);
}
@@ -424,23 +429,24 @@ static ExprResult calculateConstraintSatisfaction(
llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
- SubstitutedOuterMost,
+ SubstitutedOuterMost, Satisfaction,
PackSubstitutionIndex);
- if (!SubstitutedArgs)
- return false;
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ return ExprEmpty();
+ }
Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex);
ExprResult SubstitutedAtomicExpr =
EvaluateAtomicConstraint(S, Constraint.getConstraintExpr(), Template,
TemplateNameLoc, *SubstitutedArgs, Satisfaction);
- if (SubstitutedAtomicExpr.isInvalid()) {
- return false;
- }
+ if (SubstitutedAtomicExpr.isInvalid())
+ return ExprError();
- if (!SubstitutedAtomicExpr.isUsable())
+ if (SubstitutedAtomicExpr.isUnset())
// Evaluator has decided satisfaction without yielding an expression.
- return true;
+ return ExprEmpty();
// We don't have the ability to evaluate this, since it contains a
// RecoveryExpr, so we want to fail overload resolution. Otherwise,
@@ -462,13 +468,13 @@ static ExprResult calculateConstraintSatisfaction(
new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
SubstitutedAtomicExpr.get()->getBeginLoc(),
StringRef(Mem, MessageSize)});
- return true;
+ return SubstitutedAtomicExpr;
}
if (SubstitutedAtomicExpr.get()->isValueDependent()) {
Satisfaction.IsSatisfied = true;
Satisfaction.ContainsErrors = false;
- return true;
+ return SubstitutedAtomicExpr;
}
EnterExpressionEvaluationContext ConstantEvaluated(
@@ -486,7 +492,7 @@ static ExprResult calculateConstraintSatisfaction(
<< SubstitutedAtomicExpr.get()->getSourceRange();
for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
S.Diag(PDiag.first, PDiag.second);
- return false;
+ return ExprError();
}
assert(EvalResult.Val.isInt() &&
@@ -538,7 +544,7 @@ static ExprResult calculateConstraintSatisfaction(
bool Conjunction =
FE.getFoldOperator() == FoldExpandedConstraint::FoldOperatorKind::And;
- size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
+ unsigned EffectiveDetailEndIndex = Satisfaction.Details.size();
llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
@@ -547,45 +553,54 @@ static ExprResult calculateConstraintSatisfaction(
static_cast<const NormalizedConstraintWithParamMapping &>(
FE.getNormalizedPattern()),
// FIXME: Is PackSubstitutionIndex correct?
- Template, MLTAL, SubstitutedOuterMost, S.ArgPackSubstIndex);
- if (!SubstitutedArgs)
- return false;
+ Template, MLTAL, SubstitutedOuterMost, Satisfaction,
+ S.ArgPackSubstIndex);
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ return ExprError();
+ }
ExprResult Out;
UnsignedOrNone NumExpansions = EvaluateFoldExpandedConstraintSize(
S, FE, Template, TemplateNameLoc, *SubstitutedArgs, Satisfaction);
if (!NumExpansions)
- return false;
+ return ExprEmpty();
if (*NumExpansions == 0) {
Satisfaction.IsSatisfied = Conjunction;
- return true;
+ return ExprEmpty();
}
for (unsigned I = 0; I < *NumExpansions; I++) {
Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
Satisfaction.IsSatisfied = false;
Satisfaction.ContainsErrors = false;
- // FIXME
ExprResult Expr = calculateConstraintSatisfaction(
S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
*SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
- // SFINAE errors shouldn't prevent disjunction from evaluating
- // FIXME: Does !Success == SFINAE errors occurred?
- if (!Expr.isUsable() && Conjunction)
- return false;
+ if (Expr.isUsable()) {
+ if (Out.isUnset())
+ Out = Expr;
+ else
+ Out = BinaryOperator::Create(S.Context, Out.get(), Expr.get(),
+ Conjunction ? BinaryOperatorKind::BO_LAnd
+ : BinaryOperatorKind::BO_LOr,
+ S.Context.BoolTy, VK_PRValue, OK_Ordinary,
+ FE.getBeginLoc(), FPOptionsOverride{});
+ } else {
+ assert(!Satisfaction.IsSatisfied);
+ }
if (!Conjunction && Satisfaction.IsSatisfied) {
Satisfaction.Details.erase(Satisfaction.Details.begin() +
EffectiveDetailEndIndex,
Satisfaction.Details.end());
break;
}
+ if (Satisfaction.IsSatisfied != Conjunction)
+ return Out;
}
- // Satisfaction.IsSatisfied might be overwritten.
- // How to handle errors here ?? Shall we substitute into the concept?
- if (Satisfaction.Details.size() != EffectiveDetailEndIndex)
- Satisfaction.IsSatisfied = false;
- return true;
+
+ return Out;
}
static ExprResult calculateConstraintSatisfaction(
@@ -594,33 +609,35 @@ static ExprResult calculateConstraintSatisfaction(
ConstraintSatisfaction &Satisfaction,
UnsignedOrNone PackSubstitutionIndex) {
- Sema::ContextRAII CurContext(
- S, Constraint.getConceptId()->getNamedConcept()->getDeclContext(),
- /*NewThisContext=*/false);
-
- Sema::InstantiatingTemplate Tpl(
- S, Constraint.getConceptId()->getBeginLoc(),
+ std::optional<Sema::InstantiatingTemplate> InstTemplate;
+ InstTemplate.emplace(S, Constraint.getConceptId()->getBeginLoc(),
Sema::InstantiatingTemplate::ConstraintsCheck{},
- Constraint.getConceptId()->getNamedConcept(), MLTAL.getInnermost(),
- Constraint.getSourceRange());
+ Constraint.getConceptId()->getNamedConcept(),
+ MLTAL.getInnermost(), Constraint.getSourceRange());
- auto Size = Satisfaction.Details.size();
+ unsigned Size = Satisfaction.Details.size();
ExprResult E = calculateConstraintSatisfaction(
S, Constraint.getNormalizedConstraint(), Template, TemplateNameLoc, MLTAL,
Satisfaction, PackSubstitutionIndex);
- if (!E.isUsable())
+ if (!E.isUsable()) {
+ Satisfaction.Details.insert(Satisfaction.Details.begin() + Size,
+ Constraint.getConceptId());
return E;
+ }
llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
- SubstitutedOuterMost,
+ SubstitutedOuterMost, Satisfaction,
PackSubstitutionIndex);
- if (!SubstitutedArgs)
- return E.isUsable();
+ if (!SubstitutedArgs) {
+ Satisfaction.IsSatisfied = false;
+ // FIXME: diagnostics?
+ return ExprError();
+ }
Sema::SFINAETrap Trap(S);
Sema::ArgPackSubstIndexRAII SubstIndex(
@@ -630,7 +647,6 @@ static ExprResult calculateConstraintSatisfaction(
const ASTTemplateArgumentListInfo *Ori =
Constraint.getConceptId()->getTemplateArgsAsWritten();
- TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc);
TemplateArgumentListInfo TransArgs(Ori->LAngleLoc, Ori->RAngleLoc);
unsigned Depth = Template && Template->getTemplateDepth()
@@ -638,31 +654,63 @@ static ExprResult calculateConstraintSatisfaction(
: 0;
AdjustConstraintDepth Adjust(S, Depth);
if (Adjust.TransformTemplateArguments(Ori->getTemplateArgs(),
- Ori->NumTemplateArgs, TransArgs))
- return false;
+ Ori->NumTemplateArgs, TransArgs)) {
+ Satisfaction.IsSatisfied = false;
+ return E;
+ }
+ TemplateDeductionInfo Info(TemplateNameLoc);
+ InstTemplate.emplace(
+ S, TemplateNameLoc, Sema::InstantiatingTemplate::ConstraintSubstitution{},
+ const_cast<NamedDecl *>(Template), Info, Constraint.getSourceRange());
+
+ TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc);
if (S.SubstTemplateArguments(TransArgs.arguments(), *SubstitutedArgs,
OutArgs) ||
Trap.hasErrorOccurred()) {
- Satisfaction.ContainsErrors = true;
Satisfaction.IsSatisfied = false;
- return false;
+ if (!Trap.hasErrorOccurred())
+ return ExprError();
+
+ PartialDiagnosticAt SubstDiag{SourceLocation(),
+ PartialDiagnostic::NullDiagnostic()};
+ Info.takeSFINAEDiagnostic(SubstDiag);
+ // FIXME: Concepts: This is an unfortunate consequence of there
+ // being no serialization code for PartialDiagnostics and the fact
+ // that serializing them would likely take a lot more storage than
+ // just storing them as strings. We would still like, in the
+ // future, to serialize the proper PartialDiagnostic as serializing
+ // it as a string defeats the purpose of the diagnostic mechanism.
+ SmallString<128> DiagString;
+ DiagString = ": ";
+ SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString);
+ unsigned MessageSize = DiagString.size();
+ char *Mem = new (S.Context) char[MessageSize];
+ memcpy(Mem, DiagString.c_str(), MessageSize);
+ Satisfaction.Details.insert(
+ Satisfaction.Details.begin() + Size,
+ new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
+ SubstDiag.first, StringRef(Mem, MessageSize)});
+ return ExprError();
}
+ if (Satisfaction.IsSatisfied)
+ return E;
+
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);
+ /*DoCheckConstraintSatisfaction=*/false);
if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
- return false;
+ return E;
if (Size != Satisfaction.Details.size()) {
-
Satisfaction.Details.insert(
Satisfaction.Details.begin() + Size,
UnsatisfiedConstraintRecord(
@@ -678,7 +726,7 @@ static ExprResult calculateConstraintSatisfaction(
ConstraintSatisfaction &Satisfaction,
UnsignedOrNone PackSubstitutionIndex) {
- auto EffectiveDetailEndIndex = Satisfaction.Details.size();
+ unsigned EffectiveDetailEndIndex = Satisfaction.Details.size();
bool Conjunction =
Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction;
@@ -687,17 +735,13 @@ static ExprResult calculateConstraintSatisfaction(
S, Constraint.getLHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
PackSubstitutionIndex);
- if (Conjunction && !LHS.isUsable())
- return false;
+ if (Conjunction && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
+ return LHS;
if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied &&
!Satisfaction.ContainsErrors)
return LHS;
- if (Conjunction && LHS.isUsable() &&
- (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors))
- return LHS;
-
Satisfaction.ContainsErrors = false;
Satisfaction.IsSatisfied = false;
@@ -715,14 +759,11 @@ static ExprResult calculateConstraintSatisfaction(
if (!RHS.isUsable())
return LHS;
- return BinaryOperator::Create(
- S.Context, LHS.get(), RHS.get(),
- BinaryOperator::getOverloadedOpcode(
- Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction
- ? OO_AmpAmp
- : OO_PipePipe),
- S.Context.BoolTy, VK_PRValue, OK_Ordinary, Constraint.getBeginLoc(),
- FPOptionsOverride{});
+ return BinaryOperator::Create(S.Context, LHS.get(), RHS.get(),
+ Conjunction ? BinaryOperatorKind::BO_LAnd
+ : BinaryOperatorKind::BO_LOr,
+ S.Context.BoolTy, VK_PRValue, OK_Ordinary,
+ Constraint.getBeginLoc(), FPOptionsOverride{});
}
static ExprResult calculateConstraintSatisfaction(
@@ -842,7 +883,8 @@ bool Sema::CheckConstraintSatisfaction(
// here.
llvm::SmallVector<TemplateArgument, 4> FlattenedArgs;
for (auto List : TemplateArgsLists)
- llvm::append_range(FlattenedArgs, List.Args);
+ for (const TemplateArgument &Arg : List.Args)
+ FlattenedArgs.emplace_back(Context.getCanonicalTemplateArgument(Arg));
const NamedDecl *Owner = Template;
if (TopLevelConceptId)
@@ -1748,7 +1790,7 @@ substituteParameterMappings(Sema &S, ConceptIdConstraint &N,
/*UpdateArgsWithConversions=*/false))
return true;
TemplateArgs.replaceOutermostTemplateArguments(
- TemplateArgs.getAssociatedDecl(0).first, CTAI.CanonicalConverted);
+ TemplateArgs.getAssociatedDecl(0).first, CTAI.SugaredConverted);
}
return substituteParameterMappings(S, N.getNormalizedConstraint(),
TemplateArgs, ArgsAsWritten);
@@ -1788,7 +1830,7 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
const ConceptSpecializationExpr *CSE) {
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(),
- /*Final=*/false, CSE->getTemplateArguments(),
+ /*Final=*/true, CSE->getTemplateArguments(),
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 062367656c020..4837680a2066c 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7933,14 +7933,13 @@ Sema::BuildExprRequirement(
// be satisfied.
TemplateParameterList *TPL =
ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
- QualType MatchedType =
- Context.getReferenceQualifiedType(E).getCanonicalType();
+ QualType MatchedType = Context.getReferenceQualifiedType(E);
llvm::SmallVector<TemplateArgument, 1> Args;
Args.push_back(TemplateArgument(MatchedType));
auto *Param = cast<TemplateTypeParmDecl>(TPL->getParam(0));
- MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false);
+ MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/true);
MLTAL.addOuterRetainedLevels(TPL->getDepth());
const TypeConstraint *TC = Param->getTypeConstraint();
assert(TC && "Type Constraint cannot be null here");
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 256223e06873c..5380feef9161d 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4820,14 +4820,20 @@ ExprResult Sema::CheckConceptTemplateId(
DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc());
+ // There's a bug with CTAI.CanonicalConverted.
+ // If the template argument contains a DependentDecltypeType that includes a
+ // TypeAliasType, and the same written type had occurred previously in the
+ // source, then the DependentDecltypeType would be canonicalized to that
+ // previous type which would mess up the substitution.
+ // FIXME: Reland https://github.com/llvm/llvm-project/pull/101782 properly!
auto *CSD = ImplicitConceptSpecializationDecl::Create(
Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(),
- CTAI.CanonicalConverted);
+ CTAI.SugaredConverted);
ConstraintSatisfaction Satisfaction;
bool AreArgsDependent =
TemplateSpecializationType::anyDependentTemplateArguments(
- *TemplateArgs, CTAI.CanonicalConverted);
- MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.CanonicalConverted,
+ *TemplateArgs, CTAI.SugaredConverted);
+ MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.SugaredConverted,
/*Final=*/false);
auto *CL = ConceptReference::Create(
Context,
@@ -4849,11 +4855,11 @@ ExprResult Sema::CheckConceptTemplateId(
SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
TemplateArgs->getRAngleLoc()),
Satisfaction, CL);
+ Satisfaction.ContainsErrors = Error;
}
- if (!DoCheckConstraintSatisfaction)
- Satisfaction.IsSatisfied = false;
- Satisfaction.ContainsErrors = Error;
+ if (Error)
+ return ExprError();
return ConceptSpecializationExpr::Create(
Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction);
@@ -6122,7 +6128,7 @@ bool Sema::CheckTemplateArgumentList(
CXXThisScopeRAII Scope(*this, RD, ThisQuals, RD != nullptr);
MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
- Template, NewContext, /*Final=*/false, CTAI.CanonicalConverted,
+ Template, NewContext, /*Final=*/true, CTAI.SugaredConverted,
/*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConceptInstantiation=*/true);
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index ff873e85b07e8..0ae65c4e60279 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3216,7 +3216,7 @@ CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template,
// If we don't need to replace the deduced template arguments,
// we can add them immediately as the inner-most argument list.
if (!DeducedArgsNeedReplacement)
- Innermost = CanonicalDeducedArgs;
+ Innermost = SugaredDeducedArgs;
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
Template, Template->getDeclContext(), /*Final=*/false, Innermost,
@@ -3228,7 +3228,7 @@ CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template,
// not class-scope explicit specialization, so replace with Deduced Args
// instead of adding to inner-most.
if (!Innermost)
- MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs);
+ MLTAL.replaceInnermostTemplateArguments(Template, SugaredDeducedArgs);
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
Info.getLocation(),
@@ -4005,11 +4005,12 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
if (CheckFunctionTemplateConstraints(
Info.getLocation(),
FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(),
- CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction))
+ CTAI.SugaredConverted, Info.AssociatedConstraintsSatisfaction))
return TemplateDeductionResult::MiscellaneousDeductionFailure;
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
- Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy(
- Context, CTAI.CanonicalConverted));
+ Info.reset(
+ TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted),
+ Info.takeCanonical());
return TemplateDeductionResult::ConstraintsNotSatisfied;
}
}
@@ -5177,8 +5178,8 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
/*DefaultArgs=*/{},
/*PartialTemplateArgs=*/false, CTAI))
return true;
- MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.CanonicalConverted,
- /*Final=*/false);
+ MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.SugaredConverted,
+ /*Final=*/true);
// Build up an EvaluationContext with an ImplicitConceptSpecializationDecl so
// that the template arguments of the constraint can be preserved. For
// example:
@@ -5192,7 +5193,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
S, Sema::ExpressionEvaluationContext::Unevaluated,
ImplicitConceptSpecializationDecl::Create(
S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(),
- CTAI.CanonicalConverted));
+ CTAI.SugaredConverted));
if (S.CheckConstraintSatisfaction(
Concept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL,
TypeLoc.getLocalSourceRange(), Satisfaction))
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 6b35b205079e5..d2d0b0c2a93ba 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2417,7 +2417,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
VisitDecl(D);
llvm::SmallVector<TemplateArgument, 4> Args;
for (unsigned I = 0; I < D->NumTemplateArgs; ++I)
- Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/true));
+ Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false));
D->setTemplateArguments(Args);
}
diff --git a/clang/test/AST/ast-dump-concepts.cpp b/clang/test/AST/ast-dump-concepts.cpp
index 84d981d2ab8de..9419dba057a4e 100644
--- a/clang/test/AST/ast-dump-concepts.cpp
+++ b/clang/test/AST/ast-dump-concepts.cpp
@@ -20,8 +20,9 @@ struct Foo {
// CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'binary_concept'
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13, col:31> 'bool' Concept {{.*}} 'binary_concept'
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:13:9> col:9
- // CHECK-NEXT: | |-TemplateArgument type 'type-parameter-1-0'
- // CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0
+ // CHECK-NEXT: | |-TemplateArgument type 'R'
+ // CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0
+ // CHECK-NEXT: | | `-TemplateTypeParm {{.*}} 'R'
// CHECK-NEXT: | `-TemplateArgument type 'int'
// CHECK-NEXT: | `-BuiltinType {{.*}} 'int'
// CHECK-NEXT: |-TemplateArgument {{.*}} type 'R'
@@ -35,8 +36,9 @@ struct Foo {
// CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'unary_concept'
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13> 'bool'
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:10:9> col:9
- // CHECK-NEXT: | `-TemplateArgument type 'type-parameter-1-0'
- // CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0
+ // CHECK-NEXT: | `-TemplateArgument type 'R'
+ // CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0
+ // CHECK-NEXT: | `-TemplateTypeParm {{.*}} 'R'
template <unary_concept R>
Foo(R);
diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp
index 781fb9f28cb8d..79b605064e124 100644
--- a/clang/test/AST/ast-dump-ctad-alias.cpp
+++ b/clang/test/AST/ast-dump-ctad-alias.cpp
@@ -185,17 +185,19 @@ void foo() {
// CHECK-NEXT: | |-BinaryOperator {{.*}} 'bool' '&&'
// CHECK-NEXT: | | |-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'invocable'
// CHECK-NEXT: | | | |-ImplicitConceptSpecializationDecl {{.*}}
-// CHECK-NEXT: | | | | |-TemplateArgument type 'type-parameter-0-2'
-// CHECK-NEXT: | | | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-2' dependent depth 0 index 2
-// CHECK-NEXT: | | | | `-TemplateArgument pack '<GH124715::Packs<type-parameter-0-1...>>'
-// CHECK-NEXT: | | | | `-TemplateArgument type 'GH124715::Packs<type-parameter-0-1...>'
-// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'GH124715::Packs<type-parameter-0-1...>' dependent
-// CHECK-NEXT: | | | | |-name: 'GH124715::Packs'
+// CHECK-NEXT: | | | | |-TemplateArgument type 'U'
+// CHECK-NEXT: | | | | | `-TemplateTypeParmType {{.*}} 'U' dependent depth 0 index 2
+// CHECK-NEXT: | | | | | `-TemplateTypeParm {{.*}} 'U'
+// CHECK-NEXT: | | | | `-TemplateArgument pack '<Packs<Ts...>>'
+// CHECK-NEXT: | | | | `-TemplateArgument type 'Packs<Ts...>'
+// CHECK-NEXT: | | | | `-ElaboratedType {{.*}} 'Packs<Ts...>' sugar dependent
+// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'Packs<Ts...>' dependent
+// CHECK-NEXT: | | | | |-name: 'Packs':'GH124715::Packs' qualified
// CHECK-NEXT: | | | | | `-ClassTemplateDecl {{.*}} Packs
-// CHECK-NEXT: | | | | `-TemplateArgument pack '<type-parameter-0-1...>'
-// CHECK-NEXT: | | | | `-TemplateArgument type 'type-parameter-0-1...'
-// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'type-parameter-0-1...' dependent
-// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent contains_unexpanded_pack depth 0 index 1 pack
+// CHECK-NEXT: | | | | `-TemplateArgument type 'Ts...'
+// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'Ts...' dependent
+// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack
+// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'Ts'
// CHECK-NEXT: | | | |-TemplateArgument {{.*}} type 'U':'type-parameter-0-2'
// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'U' dependent depth 0 index 2
// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'U'
diff --git a/clang/test/CXX/drs/cwg25xx.cpp b/clang/test/CXX/drs/cwg25xx.cpp
index ced3500938cb0..0e0fc735c6843 100644
--- a/clang/test/CXX/drs/cwg25xx.cpp
+++ b/clang/test/CXX/drs/cwg25xx.cpp
@@ -256,7 +256,7 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
template<typename T>
concept NestedErrorInRequires = requires (T x) { // #cwg2565-NEIR
- requires requires (NestedErrorInRequires auto y) {
+ requires requires (NestedErrorInRequires auto y) { // #cwg2565-NEIR-inner
// since-cxx20-error at -1 {{a concept definition cannot refer to itself}}
// since-cxx20-note@#cwg2565-NEIR {{declared here}}
// since-cxx20-error at -3 {{'auto' not allowed in requires expression parameter}}
@@ -266,7 +266,7 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
static_assert(NestedErrorInRequires<int>);
// 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}}
+ // since-cxx20-note-re@#cwg2565-NEIR-inner {{because {{.*}} would be invalid: constraint depends on a previously diagnosed expression}}
#endif
} // namespace cwg2565
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
index 1253c29e14ffc..5f1243a654f54 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
@@ -35,14 +35,14 @@ using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class templ
using r2i3 = r2<D>;
using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}}
-template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
+template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}}
struct r3 {};
using r3i1 = r3<int>;
using r3i2 = r3<A>;
using r3i3 = r3<A &>;
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
-using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
+using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}}
// Non-dependent expressions
@@ -149,7 +149,7 @@ namespace std_example {
template<typename T> 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, int *>' evaluated to false}}
+ // expected-note at -1 {{because 'is_same_v<int, typename T2::inner>' evaluated to false}}
static_assert(C1<int>);
static_assert(C1<int*>);
@@ -173,9 +173,9 @@ namespace std_example {
int operator *() { return 0; }
};
static_assert(C2<T1>);
- template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}}
+ template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'T2' does not satisfy 'C2'}}
using c2c1 = C2_check<int>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}}
- using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}}
+ using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = T2]}}
template<typename T>
void g(T t) noexcept(sizeof(T) == 1) {}
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
index 48763319a4598..314425ec67c88 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
@@ -157,7 +157,7 @@ void func() {
// expected-note@#bar {{while substituting template arguments into constraint expression here}}
// expected-note@#bar {{while checking the satisfaction of nested requirement requested here}}
// expected-note@#bar {{candidate template ignored: constraints not satisfied [with T = False]}}
- // expected-note@#bar {{because 'X<SubstitutionFailureNestedRequires::ErrorExpressions_NotSF::False>::value' evaluated to false}}
+ // expected-note@#bar {{because 'X<False>::value' evaluated to false}}
bar<int>();
// expected-error at -1 {{no matching function for call to 'bar'}} \
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp
index 5199708cd8166..5dcb1880ded48 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp
@@ -39,14 +39,14 @@ using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class
template<typename T> requires requires { sizeof(T); }
// expected-note at -1{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}}
-// expected-note at -2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
+// expected-note at -2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}}
struct r3 {};
using r3i1 = r3<int>;
using r3i2 = r3<A>;
using r3i3 = r3<A &>;
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
-using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
+using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}}
template<typename T> requires requires (T t) { 0; "a"; (void)'a'; }
struct r4 {};
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
index 5433cfb21955d..28dff336d053c 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
@@ -182,14 +182,14 @@ namespace std_example {
static_assert(C1<has_inner_and_type> && C2<has_inner_and_type> && C3<has_inner_and_type>);
template<C1 T> struct C1_check {};
// expected-note at -1 {{because 'int' does not satisfy 'C1'}}
- // expected-note at -2 {{because 'std_example::has_type' does not satisfy 'C1'}}
+ // expected-note at -2 {{because 'has_type' does not satisfy 'C1'}}
template<C2 T> struct C2_check {};
- // expected-note at -1 {{because 'std_example::has_inner' does not satisfy 'C2'}}
+ // expected-note at -1 {{because 'has_inner' does not satisfy 'C2'}}
template<C3 T> struct C3_check {};
// expected-note at -1 {{because 'void' does not satisfy 'C3'}}
using c1 = C1_check<int>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}}
- using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}}
- using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}}
+ using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = has_type]}}
+ using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = has_inner]}}
using c4 = C3_check<void>; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}}
}
@@ -199,10 +199,10 @@ template <typename T> concept C = requires { requires requires { T::a; }; };
// expected-note at -1 {{because 'T::a' would be invalid: no member named 'a' in 'PR48656::T1'}}
template <C...> struct A {};
-// expected-note at -1 {{because 'PR48656::T1' does not satisfy 'C'}}
+// expected-note at -1 {{because 'T1' does not satisfy 'C'}}
struct T1 {};
-template struct A<T1>; // expected-error {{constraints not satisfied for class template 'A' [with $0 = <PR48656::T1>]}}
+template struct A<T1>; // expected-error {{constraints not satisfied for class template 'A' [with $0 = <T1>]}}
struct T2 { static constexpr bool a = false; };
template struct A<T2>;
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 424fbaabc124e..6dea0c62fe686 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
@@ -28,7 +28,7 @@ template<typename T> requires requires {
requires S<T>{};
// 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 -3{{while checking the satisfaction of nested requirement}}
// expected-note at -5{{while substituting template arguments}}
// expected-note@#F3INST{{while checking constraint satisfaction}}
// expected-note@#F3INST{{while substituting deduced template arguments into function template 'f3' [with T = int]}}
diff --git a/clang/test/CXX/temp/temp.param/p10-2a.cpp b/clang/test/CXX/temp/temp.param/p10-2a.cpp
index 4f5fdd3b4809a..c0406f88db5f3 100644
--- a/clang/test/CXX/temp/temp.param/p10-2a.cpp
+++ b/clang/test/CXX/temp/temp.param/p10-2a.cpp
@@ -86,16 +86,18 @@ using f1 = F<int>;
using f2 = F<long>; // expected-error {{constraints not satisfied for alias template 'F' [with T = long]}}
template<typename T, typename... Ts>
-concept OneOf = (is_same_v<T, Ts> || ...);
-// expected-note at -1 2{{because 'is_same_v<char, char[1]>' evaluated to false}}
-// expected-note at -2 2{{and 'is_same_v<char, char[2]>' evaluated to false}}
-// expected-note at -3 {{because 'is_same_v<short, int>' evaluated to false}}
-// expected-note at -4 {{and 'is_same_v<short, long>' evaluated to false}}
-// expected-note at -5 {{and 'is_same_v<short, char>' evaluated to false}}
-// expected-note at -6 3{{because 'is_same_v<int, char[1]>' evaluated to false}}
-// expected-note at -7 3{{and 'is_same_v<int, char[2]>' evaluated to false}}
-// expected-note at -8 2{{because 'is_same_v<std::nullptr_t, char>' evaluated to false}}
-// expected-note at -9 2{{and 'is_same_v<std::nullptr_t, int>' evaluated to false}}
+concept OneOf = (is_same_v<T, Ts> || ...); // #OneOf
+// expected-note@#OneOf 2{{because 'is_same_v<char, char[1]>' evaluated to false}}
+// expected-note@#OneOf 2{{and 'is_same_v<char, char[2]>' evaluated to false}}
+// expected-note@#OneOf {{because 'is_same_v<short, int>' evaluated to false}}
+// expected-note@#OneOf {{and 'is_same_v<short, long>' evaluated to false}}
+// expected-note@#OneOf {{and 'is_same_v<short, char>' evaluated to false}}
+// expected-note@#OneOf 3{{because 'is_same_v<int, char[1]>' evaluated to false}}
+// expected-note@#OneOf 3{{and 'is_same_v<int, char[2]>' evaluated to false}}
+// expected-note@#OneOf {{because 'is_same_v<decltype(nullptr), char>' evaluated to false}}
+// expected-note@#OneOf {{because 'is_same_v<std::nullptr_t, char>' evaluated to false}}
+// expected-note@#OneOf {{and 'is_same_v<std::nullptr_t, int>' evaluated to false}}
+// expected-note@#OneOf {{and 'is_same_v<decltype(nullptr), int>' evaluated to false}}
template<OneOf<char[1], char[2]> T, OneOf<int, long, char> U>
// expected-note at -1 2{{because 'OneOf<char, char[1], char[2]>' evaluated to false}}
@@ -124,6 +126,7 @@ using I = int;
using i1 = I<1>;
using i2 = I<'a'>;
+// FIXME: This crashes with -std=c++2c
using i3 = I<nullptr>;
// expected-error at -1 {{constraints not satisfied for alias template 'I' [with x = nullptr]}}
diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
index a2a5f1e413dcf..fe9a243e71b94 100644
--- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
+++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -std=c++2c -verify %s
template <class T> concept A = (T(), true);
-template <class T> concept C = A<T> && true;
+template <class T> concept C = A<T> && true; // #C
template <class T> concept D = A<T> && __is_same(T, int);
@@ -154,42 +154,69 @@ consteval int Or3() requires (C<typename T::type> || ... || C<typename U::type>)
static_assert(And1<>() == 1);
static_assert(And1<S>() == 1);
static_assert(And1<S, S>() == 1);
+// FIXME: The diagnostics are not so great
static_assert(And1<int>() == 1); // expected-error {{no matching function for call to 'And1'}}
- // expected-note@#and1 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int>]}}
+ // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And1<S, int>() == 1); // expected-error {{no matching function for call to 'And1'}}
- // expected-note@#and1 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <S, int>]}}
+ // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And1<int, S>() == 1); // expected-error {{no matching function for call to 'And1'}}
- // expected-note@#and1 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int, S>]}}
+ // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And2<S>() == 2);
static_assert(And2<S, S>() == 2);
-static_assert(And2<int>() == 2); // expected-error {{no matching function for call to 'And2'}} \
- // expected-note@#and2 {{candidate template ignored: failed template argument deduction}}
+static_assert(And2<int>() == 2); // expected-error {{no matching function for call to 'And2'}}
+ // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
+ // expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
+
static_assert(And2<int, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
- // expected-note@#and2 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}} \
+ // expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And2<S, int>() == 2); // expected-error {{no matching function for call to 'And2'}}
- // expected-note@#and2 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}}
+ // expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And2<int, S>() == 2); // expected-error {{no matching function for call to 'And2'}}
- // expected-note@#and2 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}}
+ // expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(And3<S>() == 3);
static_assert(And3<S, S>() == 3);
static_assert(And3<int>() == 3); // expected-error {{no matching function for call to 'And3'}}
- // expected-note@#and3 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
+ // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
+
static_assert(And3<int, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
- // expected-note@#and3 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}}
+ // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
+
static_assert(And3<S, int>() == 3); // expected-error {{no matching function for call to 'And3'}}
- // expected-note@#and3 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}}
+ // expected-note@#and3 {{because 'typename U::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
+
static_assert(And3<int, S>() == 3); // expected-error {{no matching function for call to 'And3'}}
- // expected-note@#and3 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}}
+ // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(Or1<>() == 1); // expected-error {{no matching function for call to 'Or1'}}
@@ -200,19 +227,25 @@ static_assert(Or1<S, int>() == 1);
static_assert(Or1<S, S>() == 1);
static_assert(Or1<int>() == 1); // expected-error {{no matching function for call to 'Or1'}}
// expected-note@#or1 {{candidate template ignored: constraints not satisfied}}
+ // expected-note@#or1 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(Or2<S>() == 2);
static_assert(Or2<int, S>() == 2);
static_assert(Or2<S, int>() == 2);
static_assert(Or2<S, S>() == 2);
static_assert(Or2<int>() == 2); // expected-error {{no matching function for call to 'Or2'}}
- // expected-note@#or2 {{candidate template ignored: failed template argument deduction}}
+ // expected-note@#or2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}}
+ // expected-note@#or2 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
static_assert(Or3<S>() == 3);
static_assert(Or3<int, S>() == 3);
static_assert(Or3<S, int>() == 3);
static_assert(Or3<S, S>() == 3);
static_assert(Or3<int>() == 3); // expected-error {{no matching function for call to 'Or3'}}
// expected-note@#or3 {{candidate template ignored: constraints not satisfied}}
+ // expected-note@#or3 {{because 'typename T::type' does not satisfy 'C'}}
+ // expected-note@#C {{because 'T' does not satisfy 'A'}}
}
namespace bool_conversion_break {
@@ -222,7 +255,7 @@ struct Thingy {
static constexpr int compare(const Thingy&) {return 1;}
};
template <typename ...T, typename ...U>
-void f(A<T ...> *, A<U ...> *) // expected-note {{candidate template ignored: failed template argument deduction}}
+void f(A<T ...> *, A<U ...> *) // expected-note {{candidate template ignored: constraints not satisfied}}
requires (T::compare(U{}) && ...); // expected-error {{atomic constraint must be of type 'bool' (found 'int')}}
void g() {
diff --git a/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp b/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp
index e9a4a1f4828fd..8400340d19f93 100644
--- a/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp
+++ b/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp
@@ -17,9 +17,9 @@ constexpr bool A<x>::far() {
b.data_member;
requires A<x-1>::far(); // #Invalid
// expected-error@#Invalid {{recursive template instantiation exceeded maximum depth}}
- // expected-note@#Invalid 2{{in instantiation}}
- // expected-note@#Invalid 1 {{while}}
+ // expected-note@#Invalid 3 {{while}}
// expected-note@#Invalid {{contexts in backtrace}}
+ // expected-note@#Invalid {{use -ftemplate-depth=N to increase}}
};
}
static_assert(A<1>::far());
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 3f0124755c674..25900cca6b79a 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -5086,12 +5086,12 @@ namespace GH121278 {
#if __cplusplus >= 202002L
template <typename B, typename D>
concept C = __is_base_of(B, D);
-// expected-error at -1 {{incomplete type 'GH121278::S' used in type trait expression}}
+// expected-error at -1 {{incomplete type 'S' used in type trait expression}}
// expected-note at -2 {{while substituting template arguments into constraint expression here}}
struct T;
struct S;
bool b = C<T, S>;
-// expected-note at -1 {{while checking the satisfaction of concept 'C<GH121278::T, GH121278::S>' requested here}}
+// expected-note at -1 {{while checking the satisfaction of concept 'C<T, S>' requested here}}
#endif
}
diff --git a/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl b/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl
index d7c6876d3b9e3..999372c95554e 100644
--- a/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl
@@ -19,7 +19,7 @@ Buffer<double2> r4;
// expected-error at +4 {{constraints not satisfied for class template 'Buffer'}}
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class Buffer}}
-// expected-note@*:* {{because 'hlsl::Buffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because 'Buffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(hlsl::Buffer<int>)' evaluated to false}}
Buffer<Buffer<int> > r5;
@@ -65,7 +65,7 @@ Buffer<half[4]> r10;
typedef vector<int, 8> int8;
// expected-error at +3 {{constraints not satisfied for class template 'Buffer'}}
-// expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because 'int8' (aka 'vector<int, 8>') does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<int, 8>)' evaluated to false}}
Buffer<int8> r11;
@@ -90,7 +90,7 @@ enum numbers { one, two, three };
Buffer<numbers> r15;
// expected-error at +3 {{constraints not satisfied for class template 'Buffer'}}
-// expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because 'double3' (aka 'vector<double, 3>') does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<double, 3>)' evaluated to false}}
Buffer<double3> r16;
diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
index 361f4303b4961..b33f2af8a1117 100644
--- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl
@@ -19,7 +19,7 @@ RWBuffer<double2> r4;
// expected-error at +4 {{constraints not satisfied for class template 'RWBuffer'}}
// expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}}
-// expected-note@*:* {{because 'hlsl::RWBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because 'RWBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(hlsl::RWBuffer<int>)' evaluated to false}}
RWBuffer<RWBuffer<int> > r5;
@@ -65,7 +65,7 @@ RWBuffer<half[4]> r10;
typedef vector<int, 8> int8;
// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
-// expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because 'int8' (aka 'vector<int, 8>') does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<int, 8>)' evaluated to false}}
RWBuffer<int8> r11;
@@ -90,7 +90,7 @@ enum numbers { one, two, three };
RWBuffer<numbers> r15;
// expected-error at +3 {{constraints not satisfied for class template 'RWBuffer'}}
-// expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}}
+// expected-note@*:* {{because 'double3' (aka 'vector<double, 3>') does not satisfy '__is_typed_resource_element_compatible'}}
// expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<double, 3>)' evaluated to false}}
RWBuffer<double3> r16;
diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
index 097cad1a64179..73dce9317f383 100644
--- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp
+++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp
@@ -12,7 +12,7 @@ void g() {
// expected-note@#FDEF{{because 'int' does not satisfy 'c'}}
// expected-note@#CDEF{{because 'f(t)' would be invalid: no matching function for call to 'f'}}
}
-} // namespace GH53213
+} // namespace GH53213
namespace GH45736 {
struct constrained;
@@ -67,15 +67,14 @@ struct my_range{
void baz() {
auto it = begin(rng); // #BEGIN_CALL
-// expected-error@#INF_BEGIN {{satisfaction of constraint 'Inf<Inf auto>' depends on itself}}
-// expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}}
+// expected-error-re@#INF_REQ {{satisfaction of constraint {{.*}} depends on itself}}
+// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<DirectRecursiveCheck::my_range>' requested here}}
// expected-note@#INF_BEGIN_EXPR {{while checking constraint satisfaction for template 'begin<DirectRecursiveCheck::my_range>' required here}}
// expected-note@#INF_BEGIN_EXPR {{while substituting deduced template arguments into function template 'begin'}}
// expected-note@#INF_BEGIN_EXPR {{in instantiation of requirement here}}
// expected-note@#INF_REQ {{while substituting template arguments into constraint expression here}}
-// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<DirectRecursiveCheck::my_range>' requested here}}
-// expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}}
-// expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin<DirectRecursiveCheck::my_range>' required here}}
+// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<struct my_range>' requested here}}
+// expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin<struct my_range>' required here}}
// expected-note@#BEGIN_CALL {{while substituting deduced template arguments into function template}}
// Fallout of the failure is failed lookup, which is necessary to stop odd
@@ -83,6 +82,7 @@ auto it = begin(rng); // #BEGIN_CALL
// expected-error@#BEGIN_CALL {{no matching function for call to 'begin'}}
// expected-note@#NOTINF_BEGIN {{candidate function}}
// expected-note@#INF_BEGIN{{candidate template ignored: constraints not satisfied}}
+// expected-note@#INF_BEGIN{{because 'Inf auto' does not satisfy 'Inf}}
}
} // namespace DirectRecursiveCheck
@@ -100,16 +100,17 @@ namespace GH50891 {
static_assert(Numeric<Deferred>); // #STATIC_ASSERT
// expected-error@#NUMERIC{{satisfaction of constraint 'requires (T a) { foo(a); }' depends on itself}}
// expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}}
- // expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric<GH50891::Deferred>' requested here}}
- // expected-note@#OP_TO {{while substituting template arguments into constraint expression here}}
- // expected-note@#FOO_CALL {{while checking constraint satisfaction for template}}
- // expected-note@#FOO_CALL {{while substituting deduced template arguments into function template}}
- // expected-note@#FOO_CALL {{in instantiation of requirement here}}
+ // expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric<Deferred>' requested here}}
+ // expected-note@#OP_TO {{skipping 1 context}}
+ // expected-note@#FOO_CALL 2{{while checking constraint satisfaction for template}}
+ // expected-note@#FOO_CALL 2{{while substituting deduced template arguments into function template}}
+ // expected-note@#FOO_CALL 2{{in instantiation of requirement here}}
// expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}}
// expected-error@#STATIC_ASSERT {{static assertion failed}}
- // expected-note@#STATIC_ASSERT{{while checking the satisfaction of concept 'Numeric<GH50891::Deferred>' requested here}}
- // expected-note@#STATIC_ASSERT{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+ // expected-note@#STATIC_ASSERT{{while checking the satisfaction of concept 'Numeric<Deferred>' requested here}}
+ // expected-note@#STATIC_ASSERT{{because 'Deferred' does not satisfy 'Numeric'}}
+ // expected-note@#FOO_CALL{{because 'foo(a)' would be invalid}}
} // namespace GH50891
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp
index ef9b91ca0b18e..96bc5fb7de0fc 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -574,8 +574,9 @@ static_assert(x.size == 4);
// CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'U (&)[3]'
// CHECK-NEXT: | `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True'
// CHECK-NEXT: | |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
-// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-0-0'
-// CHECK-NEXT: | | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
+// CHECK-NEXT: | | `-TemplateArgument type 'T'
+// CHECK-NEXT: | | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT: | | `-TemplateTypeParm 0x{{.+}} 'T'
// CHECK-NEXT: | `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
// CHECK-NEXT: | `-TemplateTypeParm 0x{{.+}} 'T'
@@ -588,8 +589,9 @@ static_assert(x.size == 4);
// CHECK-NEXT: |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'double (&)[3]'
// CHECK-NEXT: `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True'
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28
-// CHECK-NEXT: | `-TemplateArgument type 'type-parameter-0-0'
-// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0
+// CHECK-NEXT: | `-TemplateArgument type 'T'
+// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
+// CHECK-NEXT: | `-TemplateTypeParm 0x{{.+}} 'T'
// CHECK-NEXT: `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0'
// CHECK-NEXT: `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0
// CHECK-NEXT: `-TemplateTypeParm 0x{{.+}} 'T'
@@ -660,8 +662,9 @@ Test test(42);
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} Concept {{.*}} 'Constraint' depth 0 index 1 auto:1
// CHECK-NEXT: | `-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'Constraint'
// CHECK-NEXT: | |-ImplicitConceptSpecializationDecl {{.*}}
-// CHECK-NEXT: | | |-TemplateArgument type 'type-parameter-0-1'
-// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent depth 0 index 1
+// CHECK-NEXT: | | |-TemplateArgument type 'auto:1'
+// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'auto:1' dependent depth 0 index 1
+// CHECK-NEXT: | | | `-TemplateTypeParm {{.*}} 'auto:1'
// CHECK-NEXT: | | `-TemplateArgument type 'int'
// CHECK-NEXT: | | `-BuiltinType {{.*}} 'int'
// CHECK-NEXT: | |-TemplateArgument {{.*}} type 'auto:1':'type-parameter-0-1'
diff --git a/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp b/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp
index 9bfd472de8486..e03756ea3e3a8 100644
--- a/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp
+++ b/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
-// RUN: %clang_cc1 -std=c++2c -x c++ %s -verify
template<typename...>
diff --git a/clang/test/SemaTemplate/instantiate-requires-expr.cpp b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
index e60f79230aa00..32ad5374f46a7 100644
--- a/clang/test/SemaTemplate/instantiate-requires-expr.cpp
+++ b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
@@ -72,12 +72,12 @@ namespace type_requirement {
template<typename T> requires
false_v<requires { typename T::template temp<T>; }>
- // expected-note at -1 {{because 'false_v<requires { typename type_requirement::contains_template<int>::template temp<type_requirement::contains_template<int>>; }>' evaluated to false}}
- // expected-note at -2 {{because 'false_v<requires { typename type_requirement::contains_template<short>::template temp<type_requirement::contains_template<short>>; }>' evaluated to false}}
+ // expected-note at -1 {{because 'false_v<requires { typename contains_template<int>::template temp<contains_template<int>>; }>' evaluated to false}}
+ // expected-note at -2 {{because 'false_v<requires { typename contains_template<short>::template temp<contains_template<short>>; }>' evaluated to false}}
struct r2 {};
- using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<int>]}}
- using r2i2 = r2<contains_template<short>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<short>]}}
+ using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = contains_template<int>]}}
+ using r2i2 = r2<contains_template<short>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = contains_template<short>]}}
// substitution error occurs, then requires expr is instantiated again
@@ -108,7 +108,7 @@ namespace type_requirement {
// expected-note at -1 {{because 'false_v<requires { <<error-type>>; } && requires { <<error-type>>; }>' evaluated to false}}
struct r7 {};
- using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, type_requirement::A>]}}
+ using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, A>]}}
}
namespace expr_requirement {
@@ -268,3 +268,13 @@ struct Foo {
};
} // namespace GH110785
+
+namespace sugared_instantiation {
+ template <class C1> concept C = requires { C1{}; };
+ template <class D1> concept D = requires { new D1; };
+
+ // Test that 'deduced auto' doesn't get confused with 'undeduced auto'.
+ auto f() { return 0; }
+ static_assert(requires { { f() } -> C; });
+ static_assert(requires { { f() } -> D; });
+} // namespace sugared_instantiation
diff --git a/clang/test/SemaTemplate/pr52970.cpp b/clang/test/SemaTemplate/pr52970.cpp
index 7aac5ee856593..6aabc419bd2b8 100644
--- a/clang/test/SemaTemplate/pr52970.cpp
+++ b/clang/test/SemaTemplate/pr52970.cpp
@@ -53,7 +53,7 @@ static_assert(!DotFollowingPointer::f(Bad{}), "");
#if __cplusplus >= 202002L
template <class T>
concept C = requires(T t) { t.begin(); };
- // cxx20-note at -1 {{because 't.begin()' would be invalid: member reference type 'Holder<Incomplete> *' is a pointer}}
+ // cxx20-note at -1 {{because 't.begin()' would be invalid: member reference type 'Bad' (aka 'Holder<Incomplete> *') is a pointer}}
static_assert(C<Good>);
static_assert(!C<Bad>);
>From f7e6f58e08b96accf1482fe61916ac7b4112cd22 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sun, 10 Aug 2025 14:07:44 +0200
Subject: [PATCH 27/31] fix tests in temp.constr/temp.constr.normal/p1.cpp
---
.../temp.constr/temp.constr.normal/p1.cpp | 46 +++++++++++++------
1 file changed, 33 insertions(+), 13 deletions(-)
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 62548b2c55a9b..f19492f429ed2 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
@@ -2,22 +2,29 @@
// FIXME: RUN: %clang_cc1 -std=c++2c -x c++ -verify %s
template<typename T> concept True = true;
-template<typename T> concept Foo = True<T*>;
-template<typename T> concept Bar = Foo<T&>;
-template<typename T> requires Bar<T> struct S { };
-// FIXME: GCC rejects: https://gcc.godbolt.org/z/c9G7G6PTx if the specialization is present.
+template<typename T> concept Foo = True<T*>; // #Foo
+template<typename T> concept Bar = Foo<T&>; // #Bar
+template<typename T> requires Bar<T> struct S { }; // #S
template<typename T> requires Bar<T> && true struct S<T> { };
+// expected-error at -1 {{class template partial specialization is not more specialized than the primary template}}
+// expected-error@#Foo 2{{'type name' declared as a pointer to a reference of type 'T &'}}
+// expected-note@#Foo 2{{while substituting into concept arguments here}}
+// expected-note@#Bar 2{{while substituting into concept arguments here}}
+// expected-note@#S {{template is declared here}}
+
+
template<typename T> concept True2 = sizeof(T) >= 0;
-template<typename T> concept Foo2 = True2<T*>;
-// 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&>;
+template<typename T> concept Foo2 = True2<T*>; // #Foo2
+
+template<typename T> concept Bar2 = Foo2<T&>; // #Bar2
// 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> { };
// expected-error at -1{{class template partial specialization is not more specialized than the primary template}}
-// expected-note at -2{{while calculating associated constraint of template 'S2<T>' here}}
+// expected-error@#Foo2{{'type name' declared as a pointer to a reference of type 'T &'}}
+
namespace type_pack {
template<typename... Args>
@@ -73,16 +80,29 @@ 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> // expected-note {{while calculating associated constraint of template 'S3' here}}
+template <Bar2 T, True U>
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}}
+requires true struct S3<T, U>;
+// expected-error at -1 {{class template partial specialization is not more specialized than the primary template}}
+// expected-error@#Foo2 2{{'type name' declared as a pointer to a reference of type 'T &'}}
+// expected-note@#Foo2 2{{while substituting into concept arguments here}}
+// expected-note@#Bar2 {{while substituting into concept arguments here}}
+
// Same as above, for the second position (but this was already working).
-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, Bar2 U>
+requires true struct S4; // #S4
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}}
+requires true struct S4<T, U>; // #S4-spec
+// expected-error at -1 {{class template partial specialization is not more specialized than the primary template}}
+// expected-error@#Foo2 {{'type name' declared as a pointer to a reference of type 'U &'}}
+// expected-note@#Foo2 2{{while substituting into concept arguments here}}
+// expected-note@#S4 {{template is declared here}}
+// expected-note@#S4 {{similar constraint expressions not considered equivalent}}
+// expected-note@#S4-spec {{similar constraint expression here}}
+
+
struct X {
template<int> struct Y {
>From 71f557ec58e033c5608d258affd9355a532fef8c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sun, 10 Aug 2025 16:18:28 +0200
Subject: [PATCH 28/31] fix tests
---
clang/lib/Sema/SemaConcept.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index fa649a0142645..b4fd563a5697c 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -577,7 +577,7 @@ static ExprResult calculateConstraintSatisfaction(
Satisfaction.ContainsErrors = false;
ExprResult Expr = calculateConstraintSatisfaction(
S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
- *SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
+ MLTAL, Satisfaction, UnsignedOrNone(I));
if (Expr.isUsable()) {
if (Out.isUnset())
Out = Expr;
>From a08930ee47d9eb1466d601ac3f6bcc3e944a0e3b Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 11 Aug 2025 12:20:50 +0200
Subject: [PATCH 29/31] fix rebase
---
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/AST/ASTImporter.cpp | 2 +-
clang/lib/Sema/SemaConcept.cpp | 30 ++++++++++++-------
clang/lib/Sema/SemaTemplate.cpp | 27 +++++++++--------
clang/test/AST/ast-dump-ctad-alias.cpp | 13 ++++----
.../expr.prim.req/compound-requirement.cpp | 4 +--
clang/test/SemaCXX/cxx2b-deducing-this.cpp | 8 ++---
.../SemaCXX/cxx2c-template-template-param.cpp | 4 +--
...overload-resolution-deferred-templates.cpp | 2 +-
9 files changed, 51 insertions(+), 41 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 06263a512e506..0b437213bd249 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11673,7 +11673,7 @@ class Sema final : public SemaBase {
ExprResult
CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &ConceptNameInfo,
- NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+ NamedDecl *FoundDecl, TemplateDecl *NamedConcept,
const TemplateArgumentListInfo *TemplateArgs,
bool DoCheckConstraintSatisfaction = true);
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 315ead9ef0105..aec43172833b1 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1069,7 +1069,7 @@ Error ASTNodeImporter::ImportConstraintSatisfaction(
ToSat.ContainsErrors = FromSat.ContainsErrors;
if (!ToSat.IsSatisfied) {
for (auto Record = FromSat.begin(); Record != FromSat.end(); ++Record) {
- if (Expr *E = Record->dyn_cast<Expr *>()) {
+ if (const Expr *E = Record->dyn_cast<const Expr *>()) {
ExpectedExpr ToSecondExpr = import(E);
if (!ToSecondExpr)
return ToSecondExpr.takeError();
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index b4fd563a5697c..4ec1808fd89e8 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -245,6 +245,16 @@ class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
NewTL.setNameLoc(TL.getNameLoc());
return Result;
}
+
+ bool AlreadyTransformed(QualType T) {
+ if (T.isNull())
+ return true;
+
+ if (T->isInstantiationDependentType() || T->isVariablyModifiedType() ||
+ T->containsUnexpandedParameterPack())
+ return false;
+ return true;
+ }
};
} // namespace
@@ -576,8 +586,8 @@ static ExprResult calculateConstraintSatisfaction(
Satisfaction.IsSatisfied = false;
Satisfaction.ContainsErrors = false;
ExprResult Expr = calculateConstraintSatisfaction(
- S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
- MLTAL, Satisfaction, UnsignedOrNone(I));
+ S, FE.getNormalizedPattern(), Template, TemplateNameLoc, MLTAL,
+ Satisfaction, UnsignedOrNone(I));
if (Expr.isUsable()) {
if (Out.isUnset())
Out = Expr;
@@ -697,17 +707,17 @@ static ExprResult calculateConstraintSatisfaction(
if (Satisfaction.IsSatisfied)
return E;
- CXXScopeSpec SS;
- SS.Adopt(Constraint.getConceptId()->getNestedNameSpecifierLoc());
+ 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,
+ ExprResult SubstitutedConceptId = S.CheckConceptTemplateId(
+ SS, Constraint.getConceptId()->getTemplateKWLoc(),
+ Constraint.getConceptId()->getConceptNameInfo(),
+ Constraint.getConceptId()->getFoundDecl(),
+ Constraint.getConceptId()->getNamedConcept(), &OutArgs,
/*DoCheckConstraintSatisfaction=*/false);
- if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
+ if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
return E;
if (Size != Satisfaction.Details.size()) {
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 5380feef9161d..09ed55f37fe35 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9,6 +9,7 @@
//===----------------------------------------------------------------------===//
#include "TreeTransform.h"
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@@ -1181,10 +1182,9 @@ static ExprResult formImmediatelyDeclaredConstraint(
if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) {
ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
- /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, NamedConcept,
- &ConstraintArgs,
- /*DoCheckConstraintSatisfaction=*/
- !S.inParameterMappingSubstitution());
+ /*FoundDecl=*/FoundDecl ? FoundDecl : CD, CD, &ConstraintArgs,
+ /*DoCheckConstraintSatisfaction=*/
+ !S.inParameterMappingSubstitution());
}
// We have a template template parameter
else {
@@ -4802,7 +4802,7 @@ void Sema::diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
ExprResult Sema::CheckConceptTemplateId(
const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl,
- ConceptDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs,
+ TemplateDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs,
bool DoCheckConstraintSatisfaction) {
assert(NamedConcept && "A concept template id without a template?");
@@ -4842,18 +4842,19 @@ ExprResult Sema::CheckConceptTemplateId(
ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs));
bool Error = false;
- if (!AreArgsDependent && DoCheckConstraintSatisfaction) {
+ if (const auto *Concept = dyn_cast<ConceptDecl>(NamedConcept);
+ Concept && Concept->getConstraintExpr() && !AreArgsDependent &&
+ DoCheckConstraintSatisfaction) {
- LocalInstantiationScope Scope(*this);
+ LocalInstantiationScope Scope(*this);
- EnterExpressionEvaluationContext EECtx{
- *this, ExpressionEvaluationContext::Unevaluated, CSD};
+ EnterExpressionEvaluationContext EECtx{
+ *this, ExpressionEvaluationContext::Unevaluated, CSD};
Error = CheckConstraintSatisfaction(
- NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
- MLTAL,
- SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
- TemplateArgs->getRAngleLoc()),
+ NamedConcept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL,
+ SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
+ TemplateArgs->getRAngleLoc()),
Satisfaction, CL);
Satisfaction.ContainsErrors = Error;
}
diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp
index 79b605064e124..9a3adbcb534e8 100644
--- a/clang/test/AST/ast-dump-ctad-alias.cpp
+++ b/clang/test/AST/ast-dump-ctad-alias.cpp
@@ -190,14 +190,13 @@ void foo() {
// CHECK-NEXT: | | | | | `-TemplateTypeParm {{.*}} 'U'
// CHECK-NEXT: | | | | `-TemplateArgument pack '<Packs<Ts...>>'
// CHECK-NEXT: | | | | `-TemplateArgument type 'Packs<Ts...>'
-// CHECK-NEXT: | | | | `-ElaboratedType {{.*}} 'Packs<Ts...>' sugar dependent
-// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'Packs<Ts...>' dependent
-// CHECK-NEXT: | | | | |-name: 'Packs':'GH124715::Packs' qualified
+// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'Packs<Ts...>' dependent
+// CHECK-NEXT: | | | | |-name: 'Packs':'GH124715::Packs' qualified
// CHECK-NEXT: | | | | | `-ClassTemplateDecl {{.*}} Packs
-// CHECK-NEXT: | | | | `-TemplateArgument type 'Ts...'
-// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'Ts...' dependent
-// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack
-// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'Ts'
+// CHECK-NEXT: | | | | `-TemplateArgument type 'Ts...'
+// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'Ts...' dependent
+// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack
+// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'Ts'
// CHECK-NEXT: | | | |-TemplateArgument {{.*}} type 'U':'type-parameter-0-2'
// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'U' dependent depth 0 index 2
// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'U'
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
index 5f1243a654f54..af2dce81d8a4b 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
@@ -149,7 +149,7 @@ namespace std_example {
template<typename T> 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, typename T2::inner>' evaluated to false}}
+ // expected-note at -1 {{because 'is_same_v<int, typename std_example::T2::inner>' evaluated to false}}
static_assert(C1<int>);
static_assert(C1<int*>);
@@ -160,7 +160,7 @@ namespace std_example {
template<typename T> concept C2 =
requires(T x) {
{*x} -> same_as<typename T::inner>;
- // expected-note at -1{{because 'same_as<int, typename T2::inner>' evaluated to false}}
+ // expected-note at -1{{because 'same_as<int, typename std_example::T2::inner>' evaluated to false}}
// expected-note at -2{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}}
};
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index 74b3573a0dcaa..6777dc23c44a6 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -1257,13 +1257,13 @@ void f() {
(&A::e)(a, a);
// expected-error at -1 {{no matching function for call to 'e'}} \
// expected-note@#tpl-address-e{{candidate template ignored: constraints not satisfied [with T = A, U = A]}} \
- // expected-note@#tpl-address-e{{because '__is_same(tpl_address::A, int)' evaluated to false}}
+ // expected-note@#tpl-address-e{{because '__is_same(A, int)' evaluated to false}}
(&A::e<A>)(a, 0);
(&A::e<A>)(a, a);
// expected-error at -1 {{no matching function for call to 'e'}} \
// expected-note@#tpl-address-e{{candidate template ignored: constraints not satisfied [with T = A, U = A]}} \
- // expected-note@#tpl-address-e{{because '__is_same(tpl_address::A, int)' evaluated to false}}
+ // expected-note@#tpl-address-e{{because '__is_same(A, int)' evaluated to false}}
(&A::e<A, int>)(a, 0);
@@ -1273,12 +1273,12 @@ void f() {
(&A::f<A>)(a);
// expected-error at -1 {{no matching function for call to 'f'}} \
// expected-note@#tpl-address-f{{candidate template ignored: constraints not satisfied [with T = A]}} \
- // expected-note@#tpl-address-f{{because '__is_same(tpl_address::A, int)' evaluated to false}}
+ // expected-note@#tpl-address-f{{because '__is_same(A, int)' evaluated to false}}
(&A::f)(a);
// expected-error at -1 {{no matching function for call to 'f'}} \
// expected-note@#tpl-address-f{{candidate template ignored: constraints not satisfied [with T = A]}} \
- // expected-note@#tpl-address-f{{because '__is_same(tpl_address::A, int)' evaluated to false}}
+ // expected-note@#tpl-address-f{{because '__is_same(A, int)' evaluated to false}}
(&A::g)(a);
(&A::g)(a, 0);
diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
index ed55a059bb53c..4ad3fd95039cd 100644
--- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp
+++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
@@ -106,7 +106,7 @@ concept BinaryDefaultedFalse = false;
template <template <typename...> concept C, typename T>
struct S {
- template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
+ template <C TT> // expected-note 2{{because 'int' does not satisfy 'UnaryFalse'}}
void f(TT); // expected-note {{ignored}}
void g(C auto); // expected-note {{ignored}} \
// expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
@@ -171,7 +171,7 @@ concept BinaryDefaultedFalse = false;
template <template <typename...> concept C, typename T>
struct S {
- template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
+ template <C TT> // expected-note 2{{because 'int' does not satisfy 'UnaryFalse'}}
void f(TT); // expected-note {{ignored}}
void g(C auto); // expected-note {{ignored}} \
// expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index 0e550ff87ef7b..c3bda3988484d 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -102,7 +102,7 @@ static_assert(__is_constructible(Movable, int));
// expected-error at -1 {{no matching constructor for initialization of 'Movable'}} \
// 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{{}}
// expected-note@#Movable {{'Movable' defined here}}
template <typename T>
>From df317c0d3a5d5a32be8547173a89e62c1a8b63e6 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Mon, 11 Aug 2025 17:20:53 +0800
Subject: [PATCH 30/31] Fix CG bug
---
clang/lib/Sema/SemaConcept.cpp | 1 +
clang/lib/Sema/SemaTemplateInstantiate.cpp | 34 ++++++++++------------
2 files changed, 16 insertions(+), 19 deletions(-)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 4ec1808fd89e8..07b03f694a250 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -585,6 +585,7 @@ static ExprResult calculateConstraintSatisfaction(
Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
Satisfaction.IsSatisfied = false;
Satisfaction.ContainsErrors = false;
+ // FIXME: We can save a substitution if the next constraint is an atomic.
ExprResult Expr = calculateConstraintSatisfaction(
S, FE.getNormalizedPattern(), Template, TemplateNameLoc, MLTAL,
Satisfaction, UnsignedOrNone(I));
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 02d8963885547..7dbd5ef5248d9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2939,26 +2939,18 @@ TemplateInstantiator::TransformNestedRequirement(
return Req;
}
-#if 0
- 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();
+ 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 (Constraint->isInstantiationDependent())
- return new (C) concepts::NestedRequirement(Constraint);
-#endif
-
bool Success;
Expr *NewConstraint;
TemplateDeductionInfo Info(Constraint->getBeginLoc());
@@ -2991,8 +2983,12 @@ TemplateInstantiator::TransformNestedRequirement(
// FIXME: const correctness
// MLTAL might be dependent.
- if (!NewConstraint)
+ if (!NewConstraint) {
+ if (!Satisfaction.IsSatisfied)
+ return NestedReqWithDiag(Constraint, Satisfaction);
+
NewConstraint = Constraint;
+ }
return new (C) concepts::NestedRequirement(C, NewConstraint, Satisfaction);
}
>From bb4e716389653dfba721a392f85647b9398abbf1 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 11 Aug 2025 14:48:40 +0200
Subject: [PATCH 31/31] fix crash
---
clang/include/clang/Sema/SemaConcept.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index a03620b36caec..3449897134986 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -157,8 +157,8 @@ struct NormalizedConstraint {
}
bool hasParameterMapping() const {
- assert(getKind() != ConstraintKind::Compound);
- return Atomic.Args != nullptr;
+ return getKind() != ConstraintKind::Compound
+ && Atomic.Args != nullptr;
}
const OccurenceList &mappingOccurenceList() const {
More information about the cfe-commits
mailing list