From a8c391c8509f18037d9b4f82dfa1d88d4c91ec05 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Tue, 9 Sep 2025 16:07:52 +0200 Subject: [PATCH 01/40] Use normalization for concept satisfaction --- clang/include/clang/AST/ASTConcept.h | 29 +- clang/include/clang/AST/ASTContext.h | 21 + clang/include/clang/AST/TemplateBase.h | 1 - clang/include/clang/Sema/Sema.h | 84 +- clang/include/clang/Sema/SemaConcept.h | 413 +++- clang/include/clang/Sema/Template.h | 28 +- clang/lib/AST/ASTConcept.cpp | 27 +- clang/lib/AST/ASTContext.cpp | 1 + clang/lib/AST/ASTImporter.cpp | 2 +- clang/lib/Sema/SemaConcept.cpp | 1966 +++++++++++------ clang/lib/Sema/SemaDeclCXX.cpp | 12 +- clang/lib/Sema/SemaExprCXX.cpp | 16 +- clang/lib/Sema/SemaOverload.cpp | 3 +- clang/lib/Sema/SemaTemplate.cpp | 108 +- clang/lib/Sema/SemaTemplateDeduction.cpp | 37 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 234 +- clang/lib/Sema/TreeTransform.h | 25 +- clang/lib/Serialization/ASTReaderDecl.cpp | 2 +- clang/lib/Serialization/ASTReaderStmt.cpp | 9 +- clang/lib/Serialization/ASTWriterStmt.cpp | 19 +- clang/test/AST/ast-dump-concepts.cpp | 10 +- clang/test/AST/ast-dump-ctad-alias.cpp | 21 +- clang/test/CXX/drs/cwg25xx.cpp | 14 +- .../CXX/expr/expr.prim/expr.prim.id/p3.cpp | 3 +- .../expr.prim.req/compound-requirement.cpp | 14 +- .../expr.prim.req/nested-requirement.cpp | 35 +- .../expr.prim.req/simple-requirement.cpp | 4 +- .../expr.prim.req/type-requirement.cpp | 12 +- .../constrant-satisfaction-conversions.cpp | 5 +- .../temp.constr/temp.constr.normal/p1.cpp | 59 +- clang/test/CXX/temp/temp.param/p10-2a.cpp | 23 +- clang/test/SemaCXX/cxx23-assume.cpp | 9 +- clang/test/SemaCXX/cxx2b-deducing-this.cpp | 8 +- clang/test/SemaCXX/cxx2c-fold-exprs.cpp | 202 +- .../SemaCXX/cxx2c-template-template-param.cpp | 4 +- .../invalid-requirement-requires-expr.cpp | 7 +- ...overload-resolution-deferred-templates.cpp | 3 +- clang/test/SemaCXX/type-traits.cpp | 4 +- clang/test/SemaHLSL/BuiltIns/Buffers.hlsl | 6 +- clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl | 6 +- .../SemaTemplate/concepts-recovery-expr.cpp | 32 +- .../SemaTemplate/concepts-recursive-inst.cpp | 27 +- clang/test/SemaTemplate/concepts.cpp | 14 +- clang/test/SemaTemplate/deduction-guide.cpp | 15 +- .../instantiate-abbreviated-template.cpp | 1 + .../instantiate-expanded-type-constraint.cpp | 4 +- .../instantiate-requires-expr.cpp | 20 +- .../instantiate-template-argument.cpp | 14 +- clang/test/SemaTemplate/pr52970.cpp | 2 +- 49 files changed, 2437 insertions(+), 1178 deletions(-) diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 72da0059744f2..9babfd39dd668 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -28,13 +28,23 @@ namespace clang { class ConceptDecl; class TemplateDecl; +class ConceptReference; 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 *>; + /// 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 +59,6 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode { : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {} using SubstitutionDiagnostic = std::pair; - using Detail = llvm::PointerUnion; bool IsSatisfied = false; bool ContainsErrors = false; @@ -57,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 Details; + llvm::SmallVector Details; void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) { Profile(ID, C, ConstraintOwner, TemplateArgs); @@ -75,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 *>; - /// \brief The result of a constraint satisfaction check, containing the /// necessary information to diagnose an unsatisfied constraint. /// @@ -101,6 +103,10 @@ struct ASTConstraintSatisfaction final : return getTrailingObjects() + NumRecords; } + ArrayRef records() const { + return {begin(), end()}; + } + ASTConstraintSatisfaction(const ASTContext &C, const ConstraintSatisfaction &Satisfaction); ASTConstraintSatisfaction(const ASTContext &C, @@ -282,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/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index a2c55c71e09ae..655c8522b67c4 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -51,6 +51,27 @@ class FixedPointSemantics; struct fltSemantics; template class SmallPtrSet; +template <> struct DenseMapInfo { + static FoldingSetNodeID getEmptyKey() { return FoldingSetNodeID{}; } + + static FoldingSetNodeID getTombstoneKey() { + FoldingSetNodeID id; + for (size_t i = 0; i < sizeof(id) / sizeof(unsigned); ++i) { + id.AddInteger(std::numeric_limits::max()); + } + return id; + } + + static unsigned getHashValue(const FoldingSetNodeID &Val) { + return Val.ComputeHash(); + } + + static bool isEqual(const FoldingSetNodeID &LHS, + const FoldingSetNodeID &RHS) { + return LHS == RHS; + } +}; + } // namespace llvm namespace clang { diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h index de248ac3cf703..0359f4beb6309 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 d017d1f829015..4c4b07be1b31e 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" @@ -11695,8 +11696,9 @@ class Sema final : public SemaBase { ExprResult CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, const DeclarationNameInfo &ConceptNameInfo, - NamedDecl *FoundDecl, ConceptDecl *NamedConcept, - const TemplateArgumentListInfo *TemplateArgs); + NamedDecl *FoundDecl, TemplateDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs, + bool DoCheckConstraintSatisfaction = true); void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc); void diagnoseMissingTemplateArguments(const CXXScopeSpec &SS, @@ -12009,6 +12011,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 &SugaredConverted, @@ -12783,6 +12792,9 @@ class Sema final : public SemaBase { void MarkUsedTemplateParameters(ArrayRef TemplateArgs, unsigned Depth, llvm::SmallBitVector &Used); + void MarkUsedTemplateParameters(ArrayRef TemplateArgs, + unsigned Depth, llvm::SmallBitVector &Used); + void MarkDeducedTemplateParameters(const FunctionTemplateDecl *FunctionTemplate, llvm::SmallBitVector &Deduced) { @@ -13080,6 +13092,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; @@ -13345,6 +13359,11 @@ class Sema final : public SemaBase { const MultiLevelTemplateArgumentList &TemplateArgs, TemplateArgumentListInfo &Outputs); + bool SubstTemplateArgumentsInParameterMapping( + ArrayRef Args, SourceLocation BaseLoc, + const MultiLevelTemplateArgumentList &TemplateArgs, + TemplateArgumentListInfo &Out, bool BuildPackExpansionTypes); + /// Retrieve the template argument list(s) that should be used to /// instantiate the definition of the given declaration. /// @@ -13806,6 +13825,12 @@ class Sema final : public SemaBase { CodeSynthesisContexts.back().InConstraintSubstitution; } + bool inParameterMappingSubstitution() const { + return !CodeSynthesisContexts.empty() && + CodeSynthesisContexts.back().InParameterMappingSubstitution && + !inConstraintSubstitution(); + } + using EntityPrinter = llvm::function_ref; /// \brief create a Requirement::SubstitutionDiagnostic with only a @@ -14690,6 +14715,10 @@ class Sema final : public SemaBase { SatisfactionStack.swap(NewSS); } + using ConstrainedDeclOrNestedRequirement = + llvm::PointerUnion; + /// 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 @@ -14714,44 +14743,12 @@ 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 AssociatedConstraints, const MultiLevelTemplateArgumentList &TemplateArgLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { - llvm::SmallVector 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 AssociatedConstraints, - llvm::SmallVectorImpl &ConvertedConstraints, - const MultiLevelTemplateArgumentList &TemplateArgList, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction, + 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 @@ -14817,16 +14814,17 @@ 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, + DiagnoseUnsatisfiedConstraint(const ConceptSpecializationExpr *ConstraintExpr, bool First = true); const NormalizedConstraint *getNormalizedAssociatedConstraints( - const NamedDecl *ConstrainedDecl, + ConstrainedDeclOrNestedRequirement Entity, ArrayRef AssociatedConstraints); /// \brief Check whether the given declaration's associated constraints are @@ -14851,6 +14849,9 @@ class Sema final : public SemaBase { const NamedDecl *D1, ArrayRef AC1, const NamedDecl *D2, ArrayRef AC2); + llvm::DenseMap + ConceptIdSatisfactionCache; + private: /// Caches pairs of template-like decls whose associated constraints were /// checked for subsumption and whether or not the first's constraints did in @@ -14861,7 +14862,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 NormalizationCache; + llvm::DenseMap + NormalizationCache; llvm::ContextualFoldingSet SatisfactionCache; diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h index 648a9c51ae6c1..3ba48b75c1286 100644 --- a/clang/include/clang/Sema/SemaConcept.h +++ b/clang/include/clang/Sema/SemaConcept.h @@ -16,130 +16,387 @@ #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 "clang/Sema/Ownership.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 #include 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; + + struct AtomicBits { + LLVM_PREFERRED_TYPE(ConstraintKind) + unsigned Kind : 5; + unsigned Placeholder : 1; + unsigned PackSubstitutionIndex : 26; + OccurenceList Indexes; + TemplateArgumentLoc *Args; + TemplateParameterList *ParamList; + ExprOrConcept ConstraintExpr; + const NamedDecl *ConstraintDecl; + }; + + struct FoldExpandedBits { + LLVM_PREFERRED_TYPE(ConstraintKind) + unsigned Kind : 5; + LLVM_PREFERRED_TYPE(FoldOperatorKind) + unsigned FoldOperator : 1; + unsigned Placeholder : 26; + OccurenceList Indexes; + TemplateArgumentLoc *Args; + TemplateParameterList *ParamList; + const Expr *Pattern; + const NamedDecl *ConstraintDecl; + NormalizedConstraint *Constraint; + }; + + struct ConceptIdBits : AtomicBits { + NormalizedConstraint *Sub; + + // Only used for parameter mapping. + const ConceptSpecializationExpr *CSE; + }; + + 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), + /*Placeholder=*/0, + PackIndex.toInternalRepresentation(), + /*Indexes=*/{}, + /*Args=*/nullptr, + /*ParamList=*/nullptr, + ConstraintExpr, + ConstraintDecl} {} + + NormalizedConstraint(const Expr *Pattern, FoldOperatorKind OpKind, + NormalizedConstraint *Constraint, + const NamedDecl *ConstraintDecl) + : FoldExpanded{llvm::to_underlying(ConstraintKind::FoldExpanded), + llvm::to_underlying(OpKind), + /*Placeholder=*/0, + /*Indexes=*/{}, + /*Args=*/nullptr, + /*ParamList=*/nullptr, + Pattern, + ConstraintDecl, + Constraint} {} + + 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, + CSE} {} + + NormalizedConstraint(NormalizedConstraint *LHS, CompoundConstraintKind CCK, + NormalizedConstraint *RHS) + : Compound{llvm::to_underlying(ConstraintKind::Compound), CCK, LHS, RHS} { + } -struct alignas(ConstraintAlignment) AtomicConstraint { - const Expr *ConstraintExpr; - const NamedDecl *ConstraintDecl; - std::optional> ParameterMapping; + bool hasParameterMapping() const { + return getKind() != ConstraintKind::Compound + && Atomic.Args != nullptr; + } + + const OccurenceList &mappingOccurenceList() const { + assert(hasParameterMapping() && "This constraint has no parameter mapping"); + return Atomic.Indexes; + } + + llvm::MutableArrayRef getParameterMapping() const { + return {Atomic.Args, Atomic.Indexes.count()}; + } + + TemplateParameterList *getUsedTemplateParamList() const { + return Atomic.ParamList; + } - AtomicConstraint(const Expr *ConstraintExpr, const NamedDecl *ConstraintDecl) - : ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {}; + void updateParameterMapping(OccurenceList Indexes, + llvm::MutableArrayRef 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, - 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 ParameterMapping = + getParameterMapping(); + llvm::ArrayRef 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(Atomic.Kind); + } -/// \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 getBeginLoc() const { + switch (getKind()) { + case ConstraintKind::Atomic: + return cast(Atomic.ConstraintExpr)->getBeginLoc(); + case ConstraintKind::ConceptId: + return cast(Atomic.ConstraintExpr) + ->getBeginLoc(); + case ConstraintKind::Compound: + return Compound.LHS->getBeginLoc(); + case ConstraintKind::FoldExpanded: + return FoldExpanded.Pattern->getBeginLoc(); + } + } + + SourceLocation getEndLoc() const { + switch (getKind()) { + case ConstraintKind::Atomic: + return cast(Atomic.ConstraintExpr)->getEndLoc(); + case ConstraintKind::ConceptId: + return cast(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 ACs); + static NormalizedConstraint *fromConstraintExpr(Sema &S, const NamedDecl *D, + const Expr *E, + UnsignedOrNone SubstIndex); +}; - 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; + const NormalizedConstraint &getLHS() const { return *Compound.LHS; } - llvm::PointerUnion - 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(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::getUsedTemplateParamList; + 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); } +}; - bool isAtomic() const { return llvm::isa(Constraint); } - bool isFoldExpanded() const { - return llvm::isa(Constraint); +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 isCompound() const { return llvm::isa(Constraint); } - CompoundConstraintKind getCompoundKind() const; + const Expr *getConstraintExpr() const { + return cast(Atomic.ConstraintExpr); + } +}; - NormalizedConstraint &getLHS() const; - NormalizedConstraint &getRHS() const; +class FoldExpandedConstraint : public NormalizedConstraintWithParamMapping { + using NormalizedConstraintWithParamMapping:: + NormalizedConstraintWithParamMapping; - AtomicConstraint *getAtomicConstraint() const; +public: + static FoldExpandedConstraint *Create(ASTContext &Ctx, const Expr *Pattern, + const NamedDecl *ConstraintDecl, + FoldOperatorKind OpKind, + NormalizedConstraint *Constraint) { + return new (Ctx) + FoldExpandedConstraint(Pattern, OpKind, Constraint, ConstraintDecl); + } - FoldExpandedConstraint *getFoldExpandedConstraint() const; + using NormalizedConstraint::hasMatchingParameterMapping; -private: - static std::optional - fromAssociatedConstraints(Sema &S, const NamedDecl *D, - ArrayRef ACs); - static std::optional - fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E); -}; + FoldOperatorKind getFoldOperator() const { + return static_cast(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 AssociatedConstraints); +class ConceptIdConstraint : public NormalizedConstraintWithParamMapping { + using NormalizedConstraintWithParamMapping:: + NormalizedConstraintWithParamMapping; + +public: + static ConceptIdConstraint *Create(ASTContext &Ctx, + const ConceptReference *ConceptId, + NormalizedConstraint *SubConstraint, + const NamedDecl *ConstraintDecl, + const ConceptSpecializationExpr *CSE, + UnsignedOrNone PackIndex) { + return new (Ctx) ConceptIdConstraint(ConceptId, ConstraintDecl, + SubConstraint, CSE, PackIndex); + } + + const ConceptSpecializationExpr *getConceptSpecializationExpr() const { + return ConceptId.CSE; + } + + const ConceptReference *getConceptId() const { + return cast(ConceptId.ConstraintExpr); + } + + const NormalizedConstraint &getNormalizedConstraint() const { + return *ConceptId.Sub; + } + + NormalizedConstraint &getNormalizedConstraint() { return *ConceptId.Sub; } +}; + +struct CachedConceptIdConstraint { +#ifndef NDEBUG + const Expr *E; +#endif + ExprResult SubstExpr; + ConstraintSatisfaction Satisfaction; +}; /// \brief SubsumptionChecker establishes subsumption /// between two set of constraints. @@ -189,13 +446,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 +464,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 ReverseMap; + llvm::DenseMap ReverseMap; // Fold expanded constraints ask us to recursively establish subsumption. // This caches the result. @@ -234,12 +491,12 @@ 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(); }; -} // clang +} // namespace clang #endif // LLVM_CLANG_SEMA_SEMACONCEPT_H diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index 115c19d4f1540..5176e4ad5a362 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -234,23 +234,39 @@ 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?"); 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; TemplateArgumentLists.push_back( - {{AssociatedDecl, /*Final=*/false}, Args}); + {{AssociatedDecl, /*Final=*/Final}, Args}); } } + 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().AssociatedDeclAndFinal.setPointer( + AssociatedDecl); + 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..d533aac707cbc 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -24,8 +24,13 @@ static void CreateUnsatisfiedConstraintRecord(const ASTContext &C, const UnsatisfiedConstraintRecord &Detail, UnsatisfiedConstraintRecord *TrailingObject) { - if (auto *E = dyn_cast(Detail)) + if (Detail.isNull()) + new (TrailingObject) UnsatisfiedConstraintRecord(nullptr); + else if (const auto *E = llvm::dyn_cast(Detail)) new (TrailingObject) UnsatisfiedConstraintRecord(E); + else if (const auto *Concept = + llvm::dyn_cast(Detail)) + new (TrailingObject) UnsatisfiedConstraintRecord(Concept); else { auto &SubstitutionDiagnostic = *cast *>(Detail); @@ -74,9 +79,10 @@ ASTConstraintSatisfaction *ASTConstraintSatisfaction::Rebuild( return new (Mem) ASTConstraintSatisfaction(C, Satisfaction); } -void ConstraintSatisfaction::Profile( - llvm::FoldingSetNodeID &ID, const ASTContext &C, - const NamedDecl *ConstraintOwner, ArrayRef TemplateArgs) { +void ConstraintSatisfaction::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &C, + const NamedDecl *ConstraintOwner, + ArrayRef TemplateArgs) { ID.AddPointer(ConstraintOwner); ID.AddInteger(TemplateArgs.size()); for (auto &Arg : TemplateArgs) @@ -116,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/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index e49ff9080571e..2a5e151e33a85 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5793,6 +5793,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/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 1c8fd83feb7f8..7eaae4488d657 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()) { + if (const Expr *E = Record->dyn_cast()) { ExpectedExpr ToSecondExpr = import(E); if (!ToSecondExpr) return ToSecondExpr.takeError(); diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index d238b7916a330..22b131f4d48c2 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -12,10 +12,18 @@ #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/DependenceFlags.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprConcepts.h" +#include "clang/AST/RecursiveASTVisitor.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 +32,19 @@ #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/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include #include using namespace clang; @@ -85,7 +103,7 @@ class LogicalBinOp { OK_Ordinary, Loc, FPOptionsOverride{}); } }; -} +} // namespace bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, Token NextToken, bool *PossibleNonPrimary, @@ -146,14 +164,14 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) { Diag(ConstraintExpression->getExprLoc(), - diag::err_non_bool_atomic_constraint) << Type - << ConstraintExpression->getSourceRange(); + diag::err_non_bool_atomic_constraint) + << Type << ConstraintExpression->getSourceRange(); CheckForNonPrimary(); return false; } if (PossibleNonPrimary) - *PossibleNonPrimary = false; + *PossibleNonPrimary = false; return true; } @@ -164,52 +182,290 @@ struct SatisfactionStackRAII { SatisfactionStackRAII(Sema &SemaRef, const NamedDecl *ND, const llvm::FoldingSetNodeID &FSNID) : SemaRef(SemaRef) { - if (ND) { + if (ND) { SemaRef.PushSatisfactionStackEntry(ND, FSNID); Inserted = true; - } + } } ~SatisfactionStackRAII() { - if (Inserted) - SemaRef.PopSatisfactionStackEntry(); + if (Inserted) + SemaRef.PopSatisfactionStackEntry(); } }; } // namespace -static bool -DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID, - const NamedDecl *Templ, const Expr *E, - const MultiLevelTemplateArgumentList &MLTAL) { +static bool DiagRecursiveConstraintEval( + Sema &S, llvm::FoldingSetNodeID &ID, const NamedDecl *Templ, const Expr *E, + const MultiLevelTemplateArgumentList *MLTAL = nullptr) { E->Profile(ID, S.Context, /*Canonical=*/true); - for (const auto &List : MLTAL) - for (const auto &TemplateArg : List.Args) - TemplateArg.Profile(ID, S.Context); - - // Note that we have to do this with our own collection, because there are - // times where a constraint-expression check can cause us to need to evaluate - // other constriants that are unrelated, such as when evaluating a recovery - // expression, or when trying to determine the constexpr-ness of special - // members. Otherwise we could just use the - // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function. + if (MLTAL) { + for (const auto &List : *MLTAL) + for (const auto &TemplateArg : List.Args) + S.Context.getCanonicalTemplateArgument(TemplateArg) + .Profile(ID, S.Context); + } if (S.SatisfactionStackContains(Templ, ID)) { S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self) << const_cast(E) << E->getSourceRange(); return true; } - return false; } -static ExprResult EvaluateAtomicConstraint( - Sema &S, const Expr *AtomicExpr, const NamedDecl *Template, - SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, - ConstraintSatisfaction &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 { + unsigned TemplateDepth = 0; + +public: + using inherited = TreeTransform; + 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( + TransformDecl(TL.getNameLoc(), OldTTPDecl)); + + QualType Result = getSema().Context.getTemplateTypeParmType( + T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(), + NewTTPDecl); + TemplateTypeParmTypeLoc NewTL = TLB.push(Result); + 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 + +namespace { + +class HashParameterMapping : public RecursiveASTVisitor { + using inherited = RecursiveASTVisitor; + friend inherited; + + Sema &SemaRef; + const MultiLevelTemplateArgumentList &TemplateArgs; + llvm::FoldingSetNodeID &ID; + llvm::SmallVector UsedTemplateArgs; + + UnsignedOrNone OuterPackSubstIndex; + + TemplateArgument getPackSubstitutedTemplateArgument(TemplateArgument Arg) { + assert(*SemaRef.ArgPackSubstIndex < Arg.pack_size()); + Arg = Arg.pack_begin()[*SemaRef.ArgPackSubstIndex]; + if (Arg.isPackExpansion()) + Arg = Arg.getPackExpansionPattern(); + return Arg; + } + + bool shouldVisitTemplateInstantiations() const { return true; } + +public: + HashParameterMapping(Sema &SemaRef, + const MultiLevelTemplateArgumentList &TemplateArgs, + llvm::FoldingSetNodeID &ID, + UnsignedOrNone OuterPackSubstIndex) + : SemaRef(SemaRef), TemplateArgs(TemplateArgs), ID(ID), + OuterPackSubstIndex(OuterPackSubstIndex) {} + + bool VisitTemplateTypeParmType(TemplateTypeParmType *T) { + // A lambda expression can introduce template parameters that don't have + // corresponding template arguments yet. + if (T->getDepth() >= TemplateArgs.getNumLevels()) + return true; + + TemplateArgument Arg = TemplateArgs(T->getDepth(), T->getIndex()); + + if (T->isParameterPack() && SemaRef.ArgPackSubstIndex) { + assert(Arg.getKind() == TemplateArgument::Pack && + "Missing argument pack"); + + Arg = getPackSubstitutedTemplateArgument(Arg); + } + + UsedTemplateArgs.push_back( + SemaRef.Context.getCanonicalTemplateArgument(Arg)); + return true; + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + NamedDecl *D = E->getDecl(); + NonTypeTemplateParmDecl *NTTP = dyn_cast(D); + if (!NTTP) + return TraverseDecl(D); + + TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition()); + if (NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex) { + assert(Arg.getKind() == TemplateArgument::Pack && + "Missing argument pack"); + Arg = getPackSubstitutedTemplateArgument(Arg); + } + + UsedTemplateArgs.push_back( + SemaRef.Context.getCanonicalTemplateArgument(Arg)); + return true; + } + + bool VisitTypedefType(TypedefType *TT) { + return inherited::TraverseType(TT->desugar()); + } + + bool TraverseDecl(Decl *D) { + if (auto *VD = dyn_cast(D)) + return TraverseType(VD->getType()); + + return inherited::TraverseDecl(D); + } + + bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) { + // We don't care about TypeLocs. So traverse Types instead. + return TraverseType(TL.getType(), TraverseQualifier); + } + + bool TraverseTemplateArgument(const TemplateArgument &Arg) { + if (!Arg.containsUnexpandedParameterPack() || Arg.isPackExpansion()) { + // Act as if we are fully expanding this pack, if it is a PackExpansion. + Sema::ArgPackSubstIndexRAII _1(SemaRef, std::nullopt); + llvm::SaveAndRestore _2(OuterPackSubstIndex, + std::nullopt); + return inherited::TraverseTemplateArgument(Arg); + } + + Sema::ArgPackSubstIndexRAII _1(SemaRef, OuterPackSubstIndex); + return inherited::TraverseTemplateArgument(Arg); + } + + void VisitConstraint(const NormalizedConstraintWithParamMapping &Constraint) { + if (!Constraint.hasParameterMapping()) { + for (auto List : TemplateArgs) + for (const TemplateArgument &Arg : List.Args) + SemaRef.Context.getCanonicalTemplateArgument(Arg).Profile( + ID, SemaRef.Context); + return; + } + + llvm::ArrayRef Mapping = + Constraint.getParameterMapping(); + for (auto &ArgLoc : Mapping) { + TemplateArgument Canonical = + SemaRef.Context.getCanonicalTemplateArgument(ArgLoc.getArgument()); + // We don't want sugars to impede the profile of cache. + UsedTemplateArgs.push_back(Canonical); + TraverseTemplateArgument(Canonical); + } + + // llvm::SmallSet ArgHash; + for (auto &Used : UsedTemplateArgs) { + llvm::FoldingSetNodeID R; + Used.Profile(R, SemaRef.Context); + ID.AddNodeID(R); + // ArgHash.insert(R); + } + + // for (const llvm::FoldingSetNodeID &V : ArgHash) + // ID.AddNodeID(V); + } +}; + +class CalculateConstraintSatisfaction { + Sema &S; + const NamedDecl *Template; + SourceLocation TemplateNameLoc; + UnsignedOrNone PackSubstitutionIndex; + + ConstraintSatisfaction &Satisfaction; + +private: + ExprResult + EvaluateAtomicConstraint(const Expr *AtomicExpr, + const MultiLevelTemplateArgumentList &MLTAL); + + UnsignedOrNone EvaluateFoldExpandedConstraintSize( + const FoldExpandedConstraint &FE, + const MultiLevelTemplateArgumentList &MLTAL); + + // XXX: It is SLOW! Use it very carefully. + std::optional SubstitutionInTemplateArguments( + const NormalizedConstraintWithParamMapping &Constraint, + MultiLevelTemplateArgumentList MLTAL, + llvm::SmallVector &SubstitutedOuterMost); + + ExprResult CalculateSlow(const AtomicConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL); + + ExprResult Calculate(const AtomicConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL); + + ExprResult Calculate(const FoldExpandedConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL); + + ExprResult Calculate(const ConceptIdConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL); + + ExprResult Calculate(const CompoundConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL); + +public: + CalculateConstraintSatisfaction(Sema &SemaRef, const NamedDecl *Template, + SourceLocation TemplateNameLoc, + UnsignedOrNone PackSubstitutionIndex, + ConstraintSatisfaction &Satisfaction) + : S(SemaRef), Template(Template), TemplateNameLoc(TemplateNameLoc), + PackSubstitutionIndex(PackSubstitutionIndex), + Satisfaction(Satisfaction) {} + + ExprResult Calculate(const NormalizedConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL); +}; + +} // namespace + +ExprResult CalculateConstraintSatisfaction::EvaluateAtomicConstraint( + const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL) { EnterExpressionEvaluationContext ConstantEvaluated( 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; + ExprResult SubstitutedExpression = const_cast(AtomicExpr); { TemplateDeductionInfo Info(TemplateNameLoc); Sema::InstantiatingTemplate Inst( @@ -220,16 +476,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); SubstitutedExpression = @@ -289,284 +535,523 @@ static ExprResult EvaluateAtomicConstraint( return SubstitutedExpression; } -static UnsignedOrNone EvaluateFoldExpandedConstraintSize( - Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template, - SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, - ConstraintSatisfaction &Satisfaction) { +std::optional +CalculateConstraintSatisfaction::SubstitutionInTemplateArguments( + const NormalizedConstraintWithParamMapping &Constraint, + MultiLevelTemplateArgumentList MLTAL, + llvm::SmallVector &SubstitutedOuterMost) { + + if (!Constraint.hasParameterMapping()) + return std::move(MLTAL); + + TemplateDeductionInfo Info(Constraint.getBeginLoc()); + Sema::InstantiatingTemplate Inst( + S, Constraint.getBeginLoc(), + Sema::InstantiatingTemplate::ConstraintSubstitution{}, + // FIXME: improve const-correctness of InstantiatingTemplate + const_cast(Template), Info, Constraint.getSourceRange()); + if (Inst.isInvalid()) + return std::nullopt; + + Sema::SFINAETrap Trap(S); + + TemplateArgumentListInfo SubstArgs; + Sema::ArgPackSubstIndexRAII SubstIndex( + S, Constraint.getPackSubstitutionIndex() + ? Constraint.getPackSubstitutionIndex() + : PackSubstitutionIndex); + + if (S.SubstTemplateArgumentsInParameterMapping( + Constraint.getParameterMapping(), Constraint.getBeginLoc(), MLTAL, + SubstArgs, /*BuildPackExpansionTypes=*/true)) { + Satisfaction.IsSatisfied = false; + return std::nullopt; + } + + Sema::CheckTemplateArgumentInfo CTAI; + auto *TD = const_cast( + cast(Constraint.getConstraintDecl())); + if (S.CheckTemplateArgumentList(TD, Constraint.getUsedTemplateParamList(), + TD->getLocation(), SubstArgs, + /*DefaultArguments=*/{}, + /*PartialTemplateArgs=*/false, CTAI)) + return std::nullopt; + NormalizedConstraint::OccurenceList Used = Constraint.mappingOccurenceList(); + SubstitutedOuterMost = + llvm::to_vector_of(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()) { + SubstitutedOuterMost[I] = Arg; + Offset = I + 1; + } else { + SubstitutedOuterMost.push_back(Arg); + Offset = SubstitutedOuterMost.size(); + } + } + if (Offset < SubstitutedOuterMost.size()) + SubstitutedOuterMost.erase(SubstitutedOuterMost.begin() + Offset); + + MLTAL.replaceOutermostTemplateArguments( + const_cast(Constraint.getConstraintDecl()), + SubstitutedOuterMost); + return std::move(MLTAL); +} + +ExprResult CalculateConstraintSatisfaction::CalculateSlow( + const AtomicConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { + + llvm::SmallVector SubstitutedOuterMost; + std::optional SubstitutedArgs = + SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); + if (!SubstitutedArgs) { + Satisfaction.IsSatisfied = false; + return ExprEmpty(); + } + + Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex); + ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint( + Constraint.getConstraintExpr(), *SubstitutedArgs); + + if (SubstitutedAtomicExpr.isInvalid()) + return ExprError(); + + if (SubstitutedAtomicExpr.isUnset()) + // Evaluator has decided satisfaction without yielding an expression. + return ExprEmpty(); + + // We don't have the ability to evaluate this, since it contains a + // RecoveryExpr, so we want to fail overload resolution. Otherwise, + // we'd potentially pick up a different overload, and cause confusing + // diagnostics. SO, add a failure detail that will cause us to make this + // overload set not viable. + if (SubstitutedAtomicExpr.get()->containsErrors()) { + Satisfaction.IsSatisfied = false; + Satisfaction.ContainsErrors = true; + + PartialDiagnostic Msg = S.PDiag(diag::note_constraint_references_error); + SmallString<128> DiagString; + DiagString = ": "; + Msg.EmitToString(S.getDiagnostics(), DiagString); + unsigned MessageSize = DiagString.size(); + char *Mem = new (S.Context) char[MessageSize]; + memcpy(Mem, DiagString.c_str(), MessageSize); + Satisfaction.Details.emplace_back( + new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ + SubstitutedAtomicExpr.get()->getBeginLoc(), + StringRef(Mem, MessageSize)}); + return SubstitutedAtomicExpr; + } + + if (SubstitutedAtomicExpr.get()->isValueDependent()) { + Satisfaction.IsSatisfied = true; + Satisfaction.ContainsErrors = false; + return SubstitutedAtomicExpr; + } + + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + SmallVector EvaluationDiags; + Expr::EvalResult EvalResult; + EvalResult.Diag = &EvaluationDiags; + if (!SubstitutedAtomicExpr.get()->EvaluateAsConstantExpr(EvalResult, + S.Context) || + !EvaluationDiags.empty()) { + // C++2a [temp.constr.atomic]p1 + // ...E shall be a constant expression of type bool. + S.Diag(SubstitutedAtomicExpr.get()->getBeginLoc(), + diag::err_non_constant_constraint_expression) + << SubstitutedAtomicExpr.get()->getSourceRange(); + for (const PartialDiagnosticAt &PDiag : EvaluationDiags) + S.Diag(PDiag.first, PDiag.second); + return ExprError(); + } + + assert(EvalResult.Val.isInt() && + "evaluating bool expression didn't produce int"); + Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue(); + if (!Satisfaction.IsSatisfied) + Satisfaction.Details.emplace_back(SubstitutedAtomicExpr.get()); + + return SubstitutedAtomicExpr; +} + +ExprResult CalculateConstraintSatisfaction::Calculate( + const AtomicConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { + + unsigned Size = Satisfaction.Details.size(); + llvm::FoldingSetNodeID ID; + UnsignedOrNone OuterPackSubstIndex = + Constraint.getPackSubstitutionIndex() + ? Constraint.getPackSubstitutionIndex() + : PackSubstitutionIndex; + // Constraint.getConstraintExpr()->Profile(ID, S.Context, /*Canonical=*/true, + // /*ProfileLambdaExpr=*/true); + auto *Previous = Constraint.getConstraintExpr(); + ID.AddPointer(Constraint.getConstraintExpr()); + ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation()); + ID.AddBoolean(Constraint.hasParameterMapping()); + HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex) + .VisitConstraint(Constraint); + + unsigned CacheKeyHash = ID.computeStableHash(); + +#define UseCache 1 + if (auto Iter = S.ConceptIdSatisfactionCache.find(ID); + Iter != S.ConceptIdSatisfactionCache.end()) { +#if UseCache + auto &Cached = Iter->second.Satisfaction; + Satisfaction.ContainsErrors = Cached.ContainsErrors; + Satisfaction.IsSatisfied = Cached.IsSatisfied; + Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, + Cached.Details.begin(), Cached.Details.end()); +#ifndef NDEBUG + if (Iter->second.E != Constraint.getConstraintExpr()) { + llvm::errs() << "CacheKey: " << CacheKeyHash << " " + << S.ConceptIdSatisfactionCache.size() << "\n"; + if (Constraint.hasParameterMapping()) { + llvm::errs() << "Mapping: "; + for (auto Arg : Constraint.getParameterMapping()) { + Arg.getArgument().print(S.getPrintingPolicy(), llvm::errs(), + /*IncludeType=*/false); + llvm::errs() << " "; + } + llvm::errs() << "\n"; + } + llvm::errs() << "Previous 1: " << Iter->second.E << " "; + Iter->second.E->printPretty(llvm::errs(), /*Helper=*/nullptr, + S.getPrintingPolicy()); + llvm::errs() << "\n"; + llvm::errs() << "Current 1: " << Constraint.getConstraintExpr() << " "; + Constraint.getConstraintExpr()->printPretty( + llvm::errs(), /*Helper=*/nullptr, S.getPrintingPolicy()); + llvm::errs() << "\n"; + } + assert(Iter->second.E == Constraint.getConstraintExpr()); +#ifndef NDEBUG + return Iter->second.SubstExpr; +#else +#endif + } + + ExprResult E = CalculateSlow(Constraint, MLTAL); + + assert(Constraint.getConstraintExpr() == Previous); + + if (auto Iter = S.ConceptIdSatisfactionCache.find(ID); + Iter != S.ConceptIdSatisfactionCache.end()) { +#if UseCache + auto &Cached = Iter->second.Satisfaction; + Satisfaction.ContainsErrors = Cached.ContainsErrors; + Satisfaction.IsSatisfied = Cached.IsSatisfied; + Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, + Cached.Details.begin(), Cached.Details.end()); +#ifndef NDEBUG + if (Iter->second.E != Constraint.getConstraintExpr()) { + llvm::errs() << "CacheKey: " << CacheKeyHash << " "; + llvm::errs() << "Previous: "; + Iter->second.E->printPretty(llvm::errs(), /*Helper=*/nullptr, + S.getPrintingPolicy()); + llvm::errs() << "\n"; + llvm::errs() << "Current: "; + Constraint.getConstraintExpr()->printPretty( + llvm::errs(), /*Helper=*/nullptr, S.getPrintingPolicy()); + llvm::errs() << "\n"; + } + assert(Iter->second.E == Constraint.getConstraintExpr()); +#ifndef NDEBUG + return Iter->second.SubstExpr; +#endif + } + + CachedConceptIdConstraint Cache; + Cache.Satisfaction.ContainsErrors = Satisfaction.ContainsErrors; + Cache.Satisfaction.IsSatisfied = Satisfaction.IsSatisfied; + std::copy(Satisfaction.Details.begin() + Size, Satisfaction.Details.end(), + std::back_inserter(Cache.Satisfaction.Details)); + Cache.SubstExpr = E; + Cache.E = Constraint.getConstraintExpr(); + S.ConceptIdSatisfactionCache.insert({ID, std::move(Cache)}); +#undef UseCache + + return E; +} + +UnsignedOrNone +CalculateConstraintSatisfaction::EvaluateFoldExpandedConstraintSize( + const FoldExpandedConstraint &FE, + const MultiLevelTemplateArgumentList &MLTAL) { // We should ignore errors in the presence of packs of different size. Sema::SFINAETrap Trap(S); - Expr *Pattern = FE->getPattern(); + Expr *Pattern = const_cast(FE.getPattern()); SmallVector Unexpanded; S.collectUnexpandedParameterPacks(Pattern, Unexpanded); assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); bool Expand = true; bool RetainExpansion = false; - UnsignedOrNone NumExpansions = FE->getNumExpansions(); + UnsignedOrNone NumExpansions(std::nullopt); if (S.CheckParameterPacksForExpansion( - FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL, - /*FailOnPackProducingTemplates=*/true, Expand, RetainExpansion, + Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL, + /*FailOnPackProducingTemplates=*/false, Expand, RetainExpansion, NumExpansions) || !Expand || RetainExpansion) return std::nullopt; if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) { - S.Diag(FE->getEllipsisLoc(), + S.Diag(Pattern->getExprLoc(), clang::diag::err_fold_expression_limit_exceeded) << *NumExpansions << S.getLangOpts().BracketDepth - << FE->getSourceRange(); - S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth); + << Pattern->getSourceRange(); + S.Diag(Pattern->getExprLoc(), diag::note_bracket_depth); 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()) +ExprResult CalculateConstraintSatisfaction::Calculate( + const FoldExpandedConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { + bool Conjunction = Constraint.getFoldOperator() == + FoldExpandedConstraint::FoldOperatorKind::And; + unsigned EffectiveDetailEndIndex = Satisfaction.Details.size(); + + llvm::SmallVector SubstitutedOuterMost; + // FIXME: Is PackSubstitutionIndex correct? + llvm::SaveAndRestore _(PackSubstitutionIndex, S.ArgPackSubstIndex); + std::optional SubstitutedArgs = + SubstitutionInTemplateArguments( + static_cast(Constraint), + MLTAL, SubstitutedOuterMost); + if (!SubstitutedArgs) { + Satisfaction.IsSatisfied = false; 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(); + UnsignedOrNone NumExpansions = + EvaluateFoldExpandedConstraintSize(Constraint, *SubstitutedArgs); + if (!NumExpansions) + return ExprEmpty(); - // 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; + if (*NumExpansions == 0) { + Satisfaction.IsSatisfied = Conjunction; + return ExprEmpty(); } - 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()); + Satisfaction.IsSatisfied = false; + Satisfaction.ContainsErrors = false; + ExprResult Expr = + CalculateConstraintSatisfaction(S, Template, TemplateNameLoc, + UnsignedOrNone(I), Satisfaction) + .Calculate(Constraint.getNormalizedPattern(), *SubstitutedArgs); + 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, + Constraint.getBeginLoc(), + FPOptionsOverride{}); + } else { + assert(!Satisfaction.IsSatisfied); } - 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 && Satisfaction.IsSatisfied) { + Satisfaction.Details.erase(Satisfaction.Details.begin() + + EffectiveDetailEndIndex, + Satisfaction.Details.end()); + break; } - if (Conjunction != IsRHSSatisfied) + if (Satisfaction.IsSatisfied != Conjunction) return Out; } - if (FE->isRightFold() && FE->getInit()) { - ExprResult Res = calculateConstraintSatisfaction( - S, FE->getInit(), Template, TemplateNameLoc, MLTAL, Satisfaction); - if (Out.isInvalid()) - return ExprError(); - - if (Out.isUnset()) - Out = Res; - else if (!Res.isUnset()) { - Out = BinaryOperator::Create( - S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy, - VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{}); - } - } - - if (Out.isUnset()) { - Satisfaction.IsSatisfied = Conjunction; - Out = S.BuildEmptyCXXFoldExpr(FE->getBeginLoc(), FE->getOperator()); - } return Out; } -static ExprResult calculateConstraintSatisfaction( - Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template, - SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, - ConstraintSatisfaction &Satisfaction) { - ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts(); +ExprResult CalculateConstraintSatisfaction::Calculate( + const ConceptIdConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { - if (LogicalBinOp BO = ConstraintExpr) - return calculateConstraintSatisfaction( - S, BO.getLHS(), BO.getOp(), BO.getRHS(), Template, TemplateNameLoc, - MLTAL, Satisfaction); + std::optional InstTemplate; + InstTemplate.emplace(S, Constraint.getConceptId()->getBeginLoc(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, + Constraint.getConceptId()->getNamedConcept(), + MLTAL.getInnermost(), Constraint.getSourceRange()); - if (auto *C = dyn_cast(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); - } + unsigned Size = Satisfaction.Details.size(); - if (auto *FE = dyn_cast(ConstraintExpr); - FE && S.getLangOpts().CPlusPlus26 && - (FE->getOperator() == BinaryOperatorKind::BO_LAnd || - FE->getOperator() == BinaryOperatorKind::BO_LOr)) { - return calculateConstraintSatisfaction(S, FE, Template, TemplateNameLoc, - MLTAL, Satisfaction); + ExprResult E = Calculate(Constraint.getNormalizedConstraint(), MLTAL); + + if (!E.isUsable()) { + Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, + Constraint.getConceptId()); + return E; } - // FIXME: We should not treat ConceptSpecializationExpr as atomic constraints. + if (Satisfaction.IsSatisfied) + return E; - // An atomic constraint expression - ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint( - S, ConstraintExpr, Template, TemplateNameLoc, MLTAL, Satisfaction); + llvm::SmallVector SubstitutedOuterMost; + std::optional SubstitutedArgs = + SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); - if (SubstitutedAtomicExpr.isInvalid()) + if (!SubstitutedArgs) { + Satisfaction.IsSatisfied = false; + // FIXME: diagnostics? return ExprError(); + } - if (!SubstitutedAtomicExpr.isUsable()) - // Evaluator has decided satisfaction without yielding an expression. - return ExprEmpty(); - - // We don't have the ability to evaluate this, since it contains a - // RecoveryExpr, so we want to fail overload resolution. Otherwise, - // we'd potentially pick up a different overload, and cause confusing - // diagnostics. SO, add a failure detail that will cause us to make this - // overload set not viable. - if (SubstitutedAtomicExpr.get()->containsErrors()) { + Sema::SFINAETrap Trap(S); + Sema::ArgPackSubstIndexRAII SubstIndex( + S, Constraint.getPackSubstitutionIndex() + ? Constraint.getPackSubstitutionIndex() + : PackSubstitutionIndex); + + const ASTTemplateArgumentListInfo *Ori = + Constraint.getConceptId()->getTemplateArgsAsWritten(); + TemplateDeductionInfo Info(TemplateNameLoc); + InstTemplate.emplace( + S, TemplateNameLoc, Sema::InstantiatingTemplate::ConstraintSubstitution{}, + const_cast(Template), Info, Constraint.getSourceRange()); + + TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc); + if (S.SubstTemplateArguments(Ori->arguments(), *SubstitutedArgs, OutArgs) || + Trap.hasErrorOccurred()) { Satisfaction.IsSatisfied = false; - Satisfaction.ContainsErrors = true; + if (!Trap.hasErrorOccurred()) + return ExprError(); - PartialDiagnostic Msg = S.PDiag(diag::note_constraint_references_error); + 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 = ": "; - Msg.EmitToString(S.getDiagnostics(), 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.emplace_back( + Satisfaction.Details.insert( + Satisfaction.Details.begin() + Size, new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ - SubstitutedAtomicExpr.get()->getBeginLoc(), - StringRef(Mem, MessageSize)}); - return SubstitutedAtomicExpr; + SubstDiag.first, StringRef(Mem, MessageSize)}); + return ExprError(); } - EnterExpressionEvaluationContext ConstantEvaluated( - S, Sema::ExpressionEvaluationContext::ConstantEvaluated); - SmallVector EvaluationDiags; - Expr::EvalResult EvalResult; - EvalResult.Diag = &EvaluationDiags; - if (!SubstitutedAtomicExpr.get()->EvaluateAsConstantExpr(EvalResult, - S.Context) || - !EvaluationDiags.empty()) { - // C++2a [temp.constr.atomic]p1 - // ...E shall be a constant expression of type bool. - S.Diag(SubstitutedAtomicExpr.get()->getBeginLoc(), - diag::err_non_constant_constraint_expression) - << SubstitutedAtomicExpr.get()->getSourceRange(); - for (const PartialDiagnosticAt &PDiag : EvaluationDiags) - S.Diag(PDiag.first, PDiag.second); - return ExprError(); + 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, + /*DoCheckConstraintSatisfaction=*/false); + + if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred()) + return E; + + if (Size != Satisfaction.Details.size()) { + Satisfaction.Details.insert( + Satisfaction.Details.begin() + Size, + UnsatisfiedConstraintRecord( + SubstitutedConceptId.getAs() + ->getConceptReference())); } + return SubstitutedConceptId; +} - assert(EvalResult.Val.isInt() && - "evaluating bool expression didn't produce int"); - Satisfaction.IsSatisfied = EvalResult.Val.getInt().getBoolValue(); - if (!Satisfaction.IsSatisfied) - Satisfaction.Details.emplace_back(SubstitutedAtomicExpr.get()); +ExprResult CalculateConstraintSatisfaction::Calculate( + const CompoundConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { - return SubstitutedAtomicExpr; + unsigned EffectiveDetailEndIndex = Satisfaction.Details.size(); + + bool Conjunction = + Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction; + + ExprResult LHS = Calculate(Constraint.getLHS(), MLTAL); + + if (Conjunction && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors)) + return LHS; + + if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied && + !Satisfaction.ContainsErrors) + return LHS; + + Satisfaction.ContainsErrors = false; + Satisfaction.IsSatisfied = false; + + ExprResult RHS = Calculate(Constraint.getRHS(), MLTAL); + + if (RHS.isUsable() && Satisfaction.IsSatisfied && + !Satisfaction.ContainsErrors) + Satisfaction.Details.erase(Satisfaction.Details.begin() + + EffectiveDetailEndIndex, + Satisfaction.Details.end()); + + if (!LHS.isUsable()) + return RHS; + + if (!RHS.isUsable()) + return LHS; + + 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( - Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc, - const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, - ConstraintSatisfaction &Satisfaction) { +ExprResult CalculateConstraintSatisfaction::Calculate( + const NormalizedConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { + switch (Constraint.getKind()) { + case NormalizedConstraint::ConstraintKind::Atomic: + return Calculate(static_cast(Constraint), MLTAL); + + case NormalizedConstraint::ConstraintKind::FoldExpanded: + return Calculate(static_cast(Constraint), + MLTAL); + + case NormalizedConstraint::ConstraintKind::ConceptId: + return Calculate(static_cast(Constraint), + MLTAL); - return calculateConstraintSatisfaction(S, ConstraintExpr, Template, - TemplateNameLoc, MLTAL, Satisfaction); + case NormalizedConstraint::ConstraintKind::Compound: + return Calculate(static_cast(Constraint), + MLTAL); + } } static bool CheckConstraintSatisfaction( Sema &S, const NamedDecl *Template, ArrayRef AssociatedConstraints, - llvm::SmallVectorImpl &Converted, const MultiLevelTemplateArgumentList &TemplateArgsLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction, + Expr **ConvertedExpr, const ConceptReference *TopLevelConceptId = nullptr) { + + if (ConvertedExpr) + *ConvertedExpr = nullptr; + if (AssociatedConstraints.empty()) { Satisfaction.IsSatisfied = true; return false; @@ -578,57 +1063,60 @@ static bool CheckConstraintSatisfaction( return false; } - ArrayRef TemplateArgs = - TemplateArgsLists.getNumSubstitutedLevels() > 0 - ? TemplateArgsLists.getOutermost() - : ArrayRef{}; - Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), - Sema::InstantiatingTemplate::ConstraintsCheck{}, - const_cast(Template), TemplateArgs, TemplateIDRange); - if (Inst.isInvalid()) + llvm::ArrayRef Args; + if (TemplateArgsLists.getNumLevels() != 0) + Args = TemplateArgsLists.getInnermost(); + + std::optional SynthesisContext; + if (!TopLevelConceptId) { + SynthesisContext.emplace(S, TemplateIDRange.getBegin(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, + const_cast(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(C), + Template, /*CSE=*/nullptr, + 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, Template, TemplateIDRange.getBegin(), + S.ArgPackSubstIndex, Satisfaction) + .Calculate(*C, TemplateArgsLists); + + 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 AssociatedConstraints, - llvm::SmallVectorImpl &ConvertedConstraints, const MultiLevelTemplateArgumentList &TemplateArgsLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { + SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction, + const ConceptReference *TopLevelConceptId, Expr **ConvertedExpr) { if (AssociatedConstraints.empty()) { OutSatisfaction.IsSatisfied = true; return false; } + const auto *Template = Entity.dyn_cast(); if (!Template) { return ::CheckConstraintSatisfaction( - *this, nullptr, AssociatedConstraints, ConvertedConstraints, - TemplateArgsLists, TemplateIDRange, OutSatisfaction); + *this, nullptr, AssociatedConstraints, TemplateArgsLists, + TemplateIDRange, OutSatisfaction, ConvertedExpr, TopLevelConceptId); } // Invalid templates could make their way here. Substituting them could result // in dependent expressions. @@ -643,10 +1131,15 @@ bool Sema::CheckConstraintSatisfaction( // here. llvm::SmallVector 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) + 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; @@ -654,10 +1147,10 @@ bool Sema::CheckConstraintSatisfaction( } auto Satisfaction = - std::make_unique(Template, FlattenedArgs); - if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints, - ConvertedConstraints, TemplateArgsLists, - TemplateIDRange, *Satisfaction)) { + std::make_unique(Owner, FlattenedArgs); + if (::CheckConstraintSatisfaction( + *this, Template, AssociatedConstraints, TemplateArgsLists, + TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) { OutSatisfaction = *Satisfaction; return true; } @@ -688,14 +1181,18 @@ bool Sema::CheckConstraintSatisfaction( const ConceptSpecializationExpr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { + llvm::SmallVector 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( @@ -854,50 +1351,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 { - unsigned TemplateDepth = 0; - public: - using inherited = TreeTransform; - 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( - TransformDecl(TL.getNameLoc(), OldTTPDecl)); - - QualType Result = getSema().Context.getTemplateTypeParmType( - T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(), - NewTTPDecl); - TemplateTypeParmTypeLoc NewTL = TLB.push(Result); - NewTL.setNameLoc(TL.getNameLoc()); - return Result; - } - }; -} // namespace - static const Expr *SubstituteConstraintExpressionWithoutSatisfaction( Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo, const Expr *ConstrExpr) { @@ -1161,73 +1614,61 @@ bool Sema::CheckFunctionTemplateConstraints( static void diagnoseUnsatisfiedRequirement(Sema &S, concepts::ExprRequirement *Req, bool First) { - assert(!Req->isSatisfied() - && "Diagnose() can only be used on an unsatisfied requirement"); + assert(!Req->isSatisfied() && + "Diagnose() can only be used on an unsatisfied requirement"); switch (Req->getSatisfactionStatus()) { - case concepts::ExprRequirement::SS_Dependent: - llvm_unreachable("Diagnosing a dependent requirement"); - break; - case concepts::ExprRequirement::SS_ExprSubstitutionFailure: { - auto *SubstDiag = Req->getExprSubstitutionDiagnostic(); - if (!SubstDiag->DiagMessage.empty()) - S.Diag(SubstDiag->DiagLoc, - diag::note_expr_requirement_expr_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity - << SubstDiag->DiagMessage; - else - S.Diag(SubstDiag->DiagLoc, - diag::note_expr_requirement_expr_unknown_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity; - break; - } - case concepts::ExprRequirement::SS_NoexceptNotMet: - S.Diag(Req->getNoexceptLoc(), - diag::note_expr_requirement_noexcept_not_met) - << (int)First << Req->getExpr(); - break; - case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: { - auto *SubstDiag = - Req->getReturnTypeRequirement().getSubstitutionDiagnostic(); - if (!SubstDiag->DiagMessage.empty()) - S.Diag(SubstDiag->DiagLoc, - diag::note_expr_requirement_type_requirement_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity - << SubstDiag->DiagMessage; - else - S.Diag(SubstDiag->DiagLoc, - diag::note_expr_requirement_type_requirement_unknown_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity; - break; - } - 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()); - break; - } - case concepts::ExprRequirement::SS_Satisfied: - llvm_unreachable("We checked this above"); + case concepts::ExprRequirement::SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + break; + case concepts::ExprRequirement::SS_ExprSubstitutionFailure: { + auto *SubstDiag = Req->getExprSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_NoexceptNotMet: + S.Diag(Req->getNoexceptLoc(), diag::note_expr_requirement_noexcept_not_met) + << (int)First << Req->getExpr(); + break; + case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: { + auto *SubstDiag = + Req->getReturnTypeRequirement().getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag( + SubstDiag->DiagLoc, + diag:: + note_expr_requirement_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: { + ConceptSpecializationExpr *ConstraintExpr = + Req->getReturnTypeRequirementSubstitutedConstraintExpr(); + S.DiagnoseUnsatisfiedConstraint(ConstraintExpr); + break; + } + case concepts::ExprRequirement::SS_Satisfied: + llvm_unreachable("We checked this above"); } } static void diagnoseUnsatisfiedRequirement(Sema &S, concepts::TypeRequirement *Req, bool First) { - assert(!Req->isSatisfied() - && "Diagnose() can only be used on an unsatisfied requirement"); + assert(!Req->isSatisfied() && + "Diagnose() can only be used on an unsatisfied requirement"); switch (Req->getSatisfactionStatus()) { case concepts::TypeRequirement::SS_Dependent: llvm_unreachable("Diagnosing a dependent requirement"); @@ -1235,9 +1676,9 @@ static void diagnoseUnsatisfiedRequirement(Sema &S, case concepts::TypeRequirement::SS_SubstitutionFailure: { auto *SubstDiag = Req->getSubstitutionDiagnostic(); if (!SubstDiag->DiagMessage.empty()) - S.Diag(SubstDiag->DiagLoc, - diag::note_type_requirement_substitution_error) << (int)First - << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage; + S.Diag(SubstDiag->DiagLoc, diag::note_type_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; else S.Diag(SubstDiag->DiagLoc, diag::note_type_requirement_unknown_substitution_error) @@ -1249,31 +1690,53 @@ static void diagnoseUnsatisfiedRequirement(Sema &S, return; } } -static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, - Expr *SubstExpr, - bool First = true); + +static void diagnoseUnsatisfiedConceptIdExpr(Sema &S, + const ConceptReference *Concept, + SourceLocation Loc, bool First) { + if (Concept->getTemplateArgsAsWritten()->NumTemplateArgs == 1) { + S.Diag( + Loc, + diag:: + note_single_arg_concept_specialization_constraint_evaluated_to_false) + << (int)First + << Concept->getTemplateArgsAsWritten()->arguments()[0].getArgument() + << Concept->getNamedConcept(); + } else { + S.Diag(Loc, diag::note_concept_specialization_constraint_evaluated_to_false) + << (int)First << Concept; + } +} + +static void diagnoseUnsatisfiedConstraintExpr( + Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc, + bool First, concepts::NestedRequirement *Req = nullptr); + +static void DiagnoseUnsatisfiedConstraint( + Sema &S, ArrayRef Records, SourceLocation Loc, + bool First = true, concepts::NestedRequirement *Req = nullptr) { + for (auto &Record : Records) { + diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req); + Loc = {}; + First = isa(Record); + } +} static void diagnoseUnsatisfiedRequirement(Sema &S, concepts::NestedRequirement *Req, bool First) { - using SubstitutionDiagnostic = std::pair; - for (auto &Record : Req->getConstraintSatisfaction()) { - if (auto *SubstDiag = Record.dyn_cast()) - S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error) - << (int)First << Req->getInvalidConstraintEntity() - << SubstDiag->second; - else - diagnoseWellFormedUnsatisfiedConstraintExpr(S, Record.dyn_cast(), - First); - First = false; - } + DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(), + Req->hasInvalidConstraint() + ? SourceLocation() + : Req->getConstraintExpr()->getExprLoc(), + First, Req); } static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, - Expr *SubstExpr, + const Expr *SubstExpr, bool First) { SubstExpr = SubstExpr->IgnoreParenImpCasts(); - if (BinaryOperator *BO = dyn_cast(SubstExpr)) { + if (const BinaryOperator *BO = dyn_cast(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 @@ -1319,7 +1782,7 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context, Expr::SE_NoSideEffects, /*InConstantContext=*/true); - if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) { + if (!SimplifiedLHS.Diag && !SimplifiedRHS.Diag) { S.Diag(SubstExpr->getBeginLoc(), diag::note_atomic_constraint_evaluated_to_false_elaborated) << (int)First << SubstExpr @@ -1334,22 +1797,6 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, default: break; } - } else if (auto *CSE = dyn_cast(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(SubstExpr)) { // FIXME: RequiresExpr should store dependent diagnostics. for (concepts::Requirement *Req : RE->getRequirements()) @@ -1364,6 +1811,10 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, break; } return; + } else if (auto *CSE = dyn_cast(SubstExpr)) { + // Drill down concept ids treated as atomic constraints + S.DiagnoseUnsatisfiedConstraint(CSE, First); + return; } else if (auto *TTE = dyn_cast(SubstExpr); TTE && TTE->getTrait() == clang::TypeTrait::BTT_IsDeducible) { assert(TTE->getNumArgs() == 2); @@ -1379,216 +1830,327 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, S.DiagnoseTypeTraitDetails(SubstExpr); } -template static void diagnoseUnsatisfiedConstraintExpr( - Sema &S, const llvm::PointerUnion &Record, - bool First = true) { - if (auto *Diag = Record.template dyn_cast()) { - S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed) - << Diag->second; + 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; } - - diagnoseWellFormedUnsatisfiedConstraintExpr(S, cast(Record), First); + if (const auto *Concept = dyn_cast(Record)) { + if (Loc.isInvalid()) + Loc = Concept->getBeginLoc(); + diagnoseUnsatisfiedConceptIdExpr(S, Concept, Loc, First); + return; + } + diagnoseWellFormedUnsatisfiedConstraintExpr( + S, cast(Record), 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"); - for (auto &Record : Satisfaction.Details) { - diagnoseUnsatisfiedConstraintExpr(*this, Record, First); - First = false; - } + ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.Details, Loc, First); } void Sema::DiagnoseUnsatisfiedConstraint( - const ASTConstraintSatisfaction &Satisfaction, - bool First) { + const ConceptSpecializationExpr *ConstraintExpr, bool First) { + + const ASTConstraintSatisfaction &Satisfaction = + ConstraintExpr->getSatisfaction(); + assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - for (auto &Record : Satisfaction) { - diagnoseUnsatisfiedConstraintExpr(*this, Record, First); - First = false; - } + + ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(), + ConstraintExpr->getBeginLoc(), First); } -const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints( - const NamedDecl *ConstrainedDecl, - ArrayRef 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(ConstrainedDecl->getCanonicalDecl()); +namespace { - auto CacheEntry = NormalizationCache.find(ConstrainedDecl); - if (CacheEntry == NormalizationCache.end()) { - auto Normalized = NormalizedConstraint::fromAssociatedConstraints( - *this, ConstrainedDecl, AssociatedConstraints); - CacheEntry = - NormalizationCache - .try_emplace(ConstrainedDecl, - Normalized - ? new (Context) NormalizedConstraint( - std::move(*Normalized)) - : nullptr) - .first; - } - return CacheEntry->second; -} +class SubstituteParameterMappings { + Sema &SemaRef; -const NormalizedConstraint *clang::getNormalizedAssociatedConstraints( - Sema &S, const NamedDecl *ConstrainedDecl, - ArrayRef AssociatedConstraints) { - return S.getNormalizedAssociatedConstraints(ConstrainedDecl, - AssociatedConstraints); -} + const MultiLevelTemplateArgumentList *MLTAL; + const ASTTemplateArgumentListInfo *ArgsAsWritten; -static bool -substituteParameterMappings(Sema &S, NormalizedConstraint &N, - ConceptDecl *Concept, - const MultiLevelTemplateArgumentList &MLTAL, - const ASTTemplateArgumentListInfo *ArgsAsWritten) { + bool InFoldExpr; - if (N.isCompound()) { - if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL, - ArgsAsWritten)) - return true; - return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL, - ArgsAsWritten); - } + SubstituteParameterMappings(Sema &SemaRef, + const MultiLevelTemplateArgumentList *MLTAL, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + bool InFoldExpr) + : SemaRef(SemaRef), MLTAL(MLTAL), ArgsAsWritten(ArgsAsWritten), + InFoldExpr(InFoldExpr) {} + + void buildParameterMapping(NormalizedConstraintWithParamMapping &N); - if (N.isFoldExpanded()) { - Sema::ArgPackSubstIndexRAII _(S, std::nullopt); - return substituteParameterMappings( - S, N.getFoldExpandedConstraint()->Constraint, Concept, MLTAL, - ArgsAsWritten); + bool substitute(NormalizedConstraintWithParamMapping &N); + + bool substitute(ConceptIdConstraint &CC); + +public: + SubstituteParameterMappings(Sema &SemaRef, bool InFoldExpr = false) + : SemaRef(SemaRef), MLTAL(nullptr), ArgsAsWritten(nullptr), + InFoldExpr(InFoldExpr) {} + + bool substitute(NormalizedConstraint &N); +}; + +void SubstituteParameterMappings::buildParameterMapping( + NormalizedConstraintWithParamMapping &N) { + TemplateParameterList *TemplateParams = + cast(N.getConstraintDecl())->getTemplateParameters(); + + llvm::SmallBitVector OccurringIndices(TemplateParams->size()); + + if (N.getKind() == NormalizedConstraint::ConstraintKind::Atomic) { + SemaRef.MarkUsedTemplateParameters( + static_cast(N).getConstraintExpr(), + /*OnlyDeduced=*/false, + /*Depth=*/0, OccurringIndices); + } else if (N.getKind() == + NormalizedConstraint::ConstraintKind::FoldExpanded) { + SemaRef.MarkUsedTemplateParameters( + static_cast(N).getPattern(), + /*OnlyDeduced=*/false, + /*Depth=*/0, OccurringIndices); + } else if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) { + auto *Args = static_cast(N) + .getConceptId() + ->getTemplateArgsAsWritten(); + if (Args) + SemaRef.MarkUsedTemplateParameters(Args->arguments(), + /*Depth=*/0, OccurringIndices); + } + TemplateArgumentLoc *TempArgs = + new (SemaRef.Context) TemplateArgumentLoc[OccurringIndices.count()]; + llvm::SmallVector UsedParams; + for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) { + SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I + ? ArgsAsWritten->arguments()[I].getLocation() + : SourceLocation(); + // FIXME: Investigate why we couldn't always preserve the SourceLoc. We + // can't assert Loc.isValid() now. + if (OccurringIndices[I]) { + NamedDecl *Param = TemplateParams->begin()[I]; + new (&(TempArgs)[J]) TemplateArgumentLoc( + SemaRef.getIdentityTemplateArgumentLoc(Param, Loc)); + UsedParams.push_back(Param); + J++; + } } + auto *UsedList = TemplateParameterList::Create( + SemaRef.Context, TemplateParams->getTemplateLoc(), + TemplateParams->getLAngleLoc(), UsedParams, + /*RAngleLoc=*/SourceLocation(), + /*RequiresClause=*/nullptr); + N.updateParameterMapping( + OccurringIndices, + MutableArrayRef{TempArgs, OccurringIndices.count()}, + UsedList); +} - TemplateParameterList *TemplateParams = Concept->getTemplateParameters(); +bool SubstituteParameterMappings::substitute( + NormalizedConstraintWithParamMapping &N) { + if (!N.hasParameterMapping()) + buildParameterMapping(N); - AtomicConstraint &Atomic = *N.getAtomicConstraint(); - TemplateArgumentListInfo SubstArgs; - if (!Atomic.ParameterMapping) { - llvm::SmallBitVector OccurringIndices(TemplateParams->size()); - S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false, - /*Depth=*/0, OccurringIndices); - 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 - // concept C = ...; - // - // template requires C - // struct S { }; - // The above currently yields a diagnostic. - // We still might have default arguments for concept parameters. - ArgsAsWritten->NumTemplateArgs > I - ? ArgsAsWritten->arguments()[I].getLocation() - : SourceLocation())); - Atomic.ParameterMapping.emplace(TempArgs, OccurringIndices.count()); - } - SourceLocation InstLocBegin = - ArgsAsWritten->arguments().empty() - ? ArgsAsWritten->getLAngleLoc() - : ArgsAsWritten->arguments().front().getSourceRange().getBegin(); - SourceLocation InstLocEnd = - ArgsAsWritten->arguments().empty() - ? ArgsAsWritten->getRAngleLoc() - : ArgsAsWritten->arguments().front().getSourceRange().getEnd(); + SourceLocation InstLocBegin, InstLocEnd; + llvm::ArrayRef Arguments = ArgsAsWritten->arguments(); + if (Arguments.empty()) { + InstLocBegin = ArgsAsWritten->getLAngleLoc(); + InstLocEnd = ArgsAsWritten->getRAngleLoc(); + } else { + auto SR = Arguments[0].getSourceRange(); + InstLocBegin = SR.getBegin(); + InstLocEnd = SR.getEnd(); + } Sema::InstantiatingTemplate Inst( - S, InstLocBegin, + SemaRef, InstLocBegin, Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, - const_cast(Atomic.ConstraintDecl), + const_cast(N.getConstraintDecl()), {InstLocBegin, InstLocEnd}); if (Inst.isInvalid()) return true; - if (S.SubstTemplateArguments(*Atomic.ParameterMapping, 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. + TemplateArgumentListInfo SubstArgs; + if (SemaRef.SubstTemplateArgumentsInParameterMapping( + N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs, + /*BuildPackExpansionTypes=*/!InFoldExpr)) + return true; + Sema::CheckTemplateArgumentInfo CTAI; + auto *TD = + const_cast(cast(N.getConstraintDecl())); + if (SemaRef.CheckTemplateArgumentList(TD, N.getUsedTemplateParamList(), + TD->getLocation(), SubstArgs, + /*DefaultArguments=*/{}, + /*PartialTemplateArgs=*/false, CTAI)) 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()); + new (SemaRef.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] = SemaRef.getTrivialTemplateArgumentLoc( + CTAI.SugaredConverted[I], QualType(), Loc); + } + + MutableArrayRef Mapping(TempArgs, + CTAI.SugaredConverted.size()); + N.updateParameterMapping(N.mappingOccurenceList(), Mapping, + N.getUsedTemplateParamList()); return false; } -static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, - const ConceptSpecializationExpr *CSE) { - MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(), - /*Final=*/false, CSE->getTemplateArguments(), - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true); +bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) { + assert(CC.getConstraintDecl() && MLTAL && ArgsAsWritten); - return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL, - CSE->getTemplateArgsAsWritten()); -} + if (substitute(static_cast(CC))) + return true; + + auto *CSE = CC.getConceptSpecializationExpr(); + assert(CSE); + assert(!CC.getBeginLoc().isInvalid()); -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); + SourceLocation InstLocBegin, InstLocEnd; + if (llvm::ArrayRef Arguments = ArgsAsWritten->arguments(); + Arguments.empty()) { + InstLocBegin = ArgsAsWritten->getLAngleLoc(); + InstLocEnd = ArgsAsWritten->getRAngleLoc(); } else { - Constraint = CompoundConstraint( - new (C) - NormalizedConstraintPair{NormalizedConstraint(C, Other.getLHS()), - NormalizedConstraint(C, Other.getRHS())}, - Other.getCompoundKind()); + auto SR = Arguments[0].getSourceRange(); + InstLocBegin = SR.getBegin(); + InstLocEnd = SR.getEnd(); } -} + // This is useful for name lookup across modules; see Sema::getLookupModules. + Sema::InstantiatingTemplate Inst( + SemaRef, InstLocBegin, + Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, + const_cast(CC.getConstraintDecl()), + {InstLocBegin, InstLocEnd}); + if (Inst.isInvalid()) + return true; -NormalizedConstraint &NormalizedConstraint::getLHS() const { - assert(isCompound() && "getLHS called on a non-compound constraint."); - return cast(Constraint).getPointer()->LHS; + TemplateArgumentListInfo 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. + const ASTTemplateArgumentListInfo *ArgsAsWritten = + CSE->getTemplateArgsAsWritten(); + if (SemaRef.SubstTemplateArgumentsInParameterMapping( + ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out, + /*BuildPackExpansionTypes=*/!InFoldExpr)) + return true; + Sema::CheckTemplateArgumentInfo CTAI; + if (SemaRef.CheckTemplateArgumentList(CSE->getNamedConcept(), + CSE->getConceptNameInfo().getLoc(), Out, + /*DefaultArgs=*/{}, + /*PartialTemplateArgs=*/false, CTAI, + /*UpdateArgsWithConversions=*/false)) + return true; + auto TemplateArgs = *MLTAL; + TemplateArgs.replaceOutermostTemplateArguments( + TemplateArgs.getAssociatedDecl(0).first, CTAI.SugaredConverted); + return SubstituteParameterMappings(SemaRef, &TemplateArgs, ArgsAsWritten, + InFoldExpr) + .substitute(CC.getNormalizedConstraint()); } -NormalizedConstraint &NormalizedConstraint::getRHS() const { - assert(isCompound() && "getRHS called on a non-compound constraint."); - return cast(Constraint).getPointer()->RHS; +bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) { + switch (N.getKind()) { + case NormalizedConstraint::ConstraintKind::Atomic: { + if (!MLTAL) { + assert(!ArgsAsWritten); + return false; + } + return substitute(static_cast(N)); + } + case NormalizedConstraint::ConstraintKind::FoldExpanded: { + auto &FE = static_cast(N); + if (!MLTAL) { + llvm::SaveAndRestore _1(InFoldExpr, true); + assert(!ArgsAsWritten); + return substitute(FE.getNormalizedPattern()); + } + Sema::ArgPackSubstIndexRAII _(SemaRef, std::nullopt); + substitute(static_cast(FE)); + return SubstituteParameterMappings(SemaRef, /*InFoldExpr=*/true) + .substitute(FE.getNormalizedPattern()); + } + case NormalizedConstraint::ConstraintKind::ConceptId: { + auto &CC = static_cast(N); + if (MLTAL) { + assert(ArgsAsWritten); + return substitute(CC); + } + assert(!ArgsAsWritten); + const ConceptSpecializationExpr *CSE = CC.getConceptSpecializationExpr(); + ConceptDecl *Concept = CSE->getNamedConcept(); + MultiLevelTemplateArgumentList MLTAL = SemaRef.getTemplateInstantiationArgs( + Concept, Concept->getLexicalDeclContext(), + /*Final=*/true, CSE->getTemplateArguments(), + /*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( + SemaRef, &MLTAL, CSE->getTemplateArgsAsWritten(), InFoldExpr) + .substitute(CC.getNormalizedConstraint()); + } + case NormalizedConstraint::ConstraintKind::Compound: { + auto &Compound = static_cast(N); + if (substitute(Compound.getLHS())) + return true; + return substitute(Compound.getRHS()); + } + } } -std::optional -NormalizedConstraint::fromAssociatedConstraints( +} // namespace + +NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints( Sema &S, const NamedDecl *D, ArrayRef 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 std::nullopt; + 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 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::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 @@ -1597,23 +2159,29 @@ 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. // 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 std::nullopt; - auto RHS = fromConstraintExpr(S, D, BO.getRHS()); + return nullptr; + auto *RHS = fromConstraintExpr(S, D, BO.getRHS(), SubstIndex); 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(E)) { - const NormalizedConstraint *SubNF; + NormalizedConstraint *SubNF; { Sema::InstantiatingTemplate Inst( S, CSE->getExprLoc(), @@ -1621,7 +2189,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, // FIXME: improve const-correctness of InstantiatingTemplate const_cast(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, @@ -1631,20 +2199,23 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, // 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(); - SubNF = S.getNormalizedAssociatedConstraints( - CD, AssociatedConstraint(CD->getConstraintExpr())); + + // Use canonical declarations to merge ConceptDecls across + // different modules. + ConceptDecl *CD = CSE->getNamedConcept()->getCanonicalDecl(); + SubNF = NormalizedConstraint::fromAssociatedConstraints( + S, CD, AssociatedConstraint(CD->getConstraintExpr(), SubstIndex)); + if (!SubNF) - return std::nullopt; + return nullptr; } + // if (substituteParameterMappings(S, *SubNF, CSE)) + // return nullptr; - std::optional New; - New.emplace(S.Context, *SubNF); - - if (substituteParameterMappings(S, *New, CSE)) - return std::nullopt; + return ConceptIdConstraint::Create(S.getASTContext(), + CSE->getConceptReference(), SubNF, D, + CSE, SubstIndex); - return New; } else if (auto *FE = dyn_cast(E); FE && S.getLangOpts().CPlusPlus26 && (FE->getOperator() == BinaryOperatorKind::BO_LAnd || @@ -1658,31 +2229,61 @@ 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 std::nullopt; + return nullptr; if (FE->isRightFold()) - RHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{ - Kind, std::move(*RHS), FE->getPattern()}}; + LHS = FoldExpandedConstraint::Create(S.getASTContext(), + FE->getPattern(), D, Kind, LHS); 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); + RHS = FoldExpandedConstraint::Create(S.getASTContext(), + FE->getPattern(), D, Kind, RHS); + + return CompoundConstraint::Create( + S.getASTContext(), LHS, + (FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction + : CCK_Disjunction), + RHS); } - auto Sub = fromConstraintExpr(S, D, FE->getPattern()); + auto *Sub = fromConstraintExpr(S, D, FE->getPattern(), SubstIndex); 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(), + D, Kind, Sub); + } + return AtomicConstraint::Create(S.getASTContext(), E, D, SubstIndex); +} + +const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints( + ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq, + ArrayRef AssociatedConstraints) { + if (!ConstrainedDeclOrNestedReq) { + auto *Normalized = NormalizedConstraint::fromAssociatedConstraints( + *this, nullptr, AssociatedConstraints); + if (!Normalized || + SubstituteParameterMappings(*this).substitute(*Normalized)) + return nullptr; + + return Normalized; } - return NormalizedConstraint{new (S.Context) AtomicConstraint(E, D)}; + // FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement! + const NamedDecl *ND = + ConstrainedDeclOrNestedReq.dyn_cast(); + 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).substitute(*Normalized)) + return nullptr; + } + return CacheEntry->second; } bool FoldExpandedConstraint::AreCompatibleForSubsumption( @@ -1693,8 +2294,10 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption( // if their respective constraints both contain an equivalent unexpanded pack. llvm::SmallVector APacks, BPacks; - Sema::collectUnexpandedParameterPacks(const_cast(A.Pattern), APacks); - Sema::collectUnexpandedParameterPacks(const_cast(B.Pattern), BPacks); + Sema::collectUnexpandedParameterPacks(const_cast(A.getPattern()), + APacks); + Sema::collectUnexpandedParameterPacks(const_cast(B.getPattern()), + BPacks); for (const UnexpandedParameterPack &APack : APacks) { auto ADI = getDepthAndIndex(APack); @@ -1788,7 +2391,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; @@ -1841,24 +2444,6 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic( return true; } -NormalizedConstraint::CompoundConstraintKind -NormalizedConstraint::getCompoundKind() const { - assert(isCompound() && "getCompoundKind on a non-compound constraint.."); - return cast(Constraint).getInt(); -} - -AtomicConstraint *NormalizedConstraint::getAtomicConstraint() const { - assert(isAtomic() && "getAtomicConstraint called on non-atomic constraint."); - return cast(Constraint); -} - -FoldExpandedConstraint * -NormalizedConstraint::getFoldExpandedConstraint() const { - assert(isFoldExpanded() && - "getFoldExpandedConstraint called on non-fold-expanded constraint."); - return cast(Constraint); -} - // // // ------------------------ Subsumption ----------------------------------- @@ -1874,8 +2459,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 [...] @@ -1891,10 +2476,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()); @@ -1912,11 +2497,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; @@ -1960,38 +2545,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(NC))}}; - FormulaType Left, Right; - SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] { - Left = Normalize(NC.getLHS()); - Right = Normalize(NC.getRHS()); - }); + case NormalizedConstraint::ConstraintKind::FoldExpanded: + return {{find(&static_cast(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( + static_cast(NC).getNormalizedConstraint()); + + case NormalizedConstraint::ConstraintKind::Compound: { + const auto &Compound = static_cast(NC); + FormulaType Left, Right; + SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] { + Left = Normalize(Compound.getLHS()); + Right = Normalize(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) { @@ -2006,12 +2600,12 @@ std::optional SubsumptionChecker::Subsumes( const NamedDecl *DP, ArrayRef P, const NamedDecl *DQ, ArrayRef 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; @@ -2061,9 +2655,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 fb57b43882911..4c55a452c244a 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17877,13 +17877,15 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, findFailedBooleanCondition(Converted.get()); if (const auto *ConceptIDExpr = dyn_cast_or_null(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(InnerCond) && !isa(InnerCond)) { Diag(InnerCond->getBeginLoc(), diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 1e8bb6e3064a9..c158712fc92c0 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7926,21 +7926,27 @@ Sema::BuildExprRequirement( // be satisfied. TemplateParameterList *TPL = ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); - QualType MatchedType = - Context.getReferenceQualifiedType(E).getCanonicalType(); + QualType MatchedType = Context.getReferenceQualifiedType(E); llvm::SmallVector Args; Args.push_back(TemplateArgument(MatchedType)); auto *Param = cast(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"); 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(Constraint.get()); + if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors) + HasError = true; + } + if (HasError) { return new (Context) concepts::ExprRequirement( createSubstDiagAt(IDC->getExprLoc(), [&](llvm::raw_ostream &OS) { @@ -7949,8 +7955,6 @@ Sema::BuildExprRequirement( }), IsSimple, NoexceptLoc, ReturnTypeRequirement); } - SubstitutedConstraintExpr = - cast(Constraint.get()); if (!SubstitutedConstraintExpr->isSatisfied()) Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied; } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index ea5c4265d736d..f741e8283761d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -12739,7 +12739,8 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, << (unsigned)FnKindPair.first << (unsigned)ocs_non_template << FnDesc /* Ignored */; ConstraintSatisfaction Satisfaction; - if (S.CheckFunctionConstraints(Fn, Satisfaction)) + if (S.CheckFunctionConstraints(Fn, Satisfaction, SourceLocation(), + /*ForOverloadResolution=*/true)) break; S.DiagnoseUnsatisfiedConstraint(Satisfaction); } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index f051a246f954f..7a84a52f7bde3 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" @@ -1188,8 +1189,9 @@ static ExprResult formImmediatelyDeclaredConstraint( if (auto *CD = dyn_cast(NamedConcept)) { ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId( SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, - /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD, - &ConstraintArgs); + /*FoundDecl=*/FoundDecl ? FoundDecl : CD, CD, &ConstraintArgs, + /*DoCheckConstraintSatisfaction=*/ + !S.inParameterMappingSubstitution()); } // We have a template template parameter else { @@ -4816,13 +4818,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, + TemplateDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs, + bool DoCheckConstraintSatisfaction) { assert(NamedConcept && "A concept template id without a template?"); if (NamedConcept->isInvalidDecl()) @@ -4839,33 +4839,48 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, 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); - LocalInstantiationScope Scope(*this); - - EnterExpressionEvaluationContext EECtx{ - *this, ExpressionEvaluationContext::Unevaluated, CSD}; - - if (!AreArgsDependent && - CheckConstraintSatisfaction( - NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()), - MLTAL, - SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(), - TemplateArgs->getRAngleLoc()), - Satisfaction)) - return ExprError(); - auto *CL = ConceptReference::Create( + auto *CL = ConceptReference::Create( Context, SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{}, TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept, ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs)); + + bool Error = false; + if (const auto *Concept = dyn_cast(NamedConcept); + Concept && Concept->getConstraintExpr() && !AreArgsDependent && + DoCheckConstraintSatisfaction) { + + LocalInstantiationScope Scope(*this); + + EnterExpressionEvaluationContext EECtx{ + *this, ExpressionEvaluationContext::Unevaluated, CSD}; + + Error = CheckConstraintSatisfaction( + NamedConcept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL, + SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(), + TemplateArgs->getRAngleLoc()), + Satisfaction, CL); + Satisfaction.ContainsErrors = Error; + } + + if (Error) + return ExprError(); + return ConceptSpecializationExpr::Create( Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction); } @@ -5183,10 +5198,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; @@ -5779,6 +5795,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; @@ -5788,8 +5818,6 @@ bool Sema::CheckTemplateArgumentList( // template. TemplateArgumentListInfo NewArgs = TemplateArgs; - TemplateParameterList *Params = GetTemplateParameterList(Template); - SourceLocation RAngleLoc = NewArgs.getRAngleLoc(); // C++23 [temp.arg.general]p1: @@ -6004,6 +6032,19 @@ 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(); @@ -6129,11 +6170,12 @@ 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); - if (EnsureTemplateArgumentListConstraints( + if (!isa(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 62e867c44ad14..38a3fb6b84b85 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3223,7 +3223,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, @@ -3235,7 +3235,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(), @@ -4012,11 +4012,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; } } @@ -5184,8 +5185,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: @@ -5199,7 +5200,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)) @@ -6725,6 +6726,7 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor { if (auto *NTTP = dyn_cast(E->getDecl())) if (NTTP->getDepth() == Depth) Used[NTTP->getIndex()] = true; + DynamicRecursiveASTVisitor::TraverseType(E->getType()); return true; } @@ -7068,10 +7070,13 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, break; case Type::UnaryTransform: - if (!OnlyDeduced) - MarkUsedTemplateParameters(Ctx, - cast(T)->getUnderlyingType(), - OnlyDeduced, Depth, Used); + if (!OnlyDeduced) { + auto *UTT = cast(T); + auto Next = UTT->getUnderlyingType(); + if (Next.isNull()) + Next = UTT->getBaseType(); + MarkUsedTemplateParameters(Ctx, Next, OnlyDeduced, Depth, Used); + } break; case Type::PackExpansion: @@ -7196,6 +7201,14 @@ void Sema::MarkUsedTemplateParameters(ArrayRef TemplateArgs, /*OnlyDeduced=*/false, Depth, Used); } +void Sema::MarkUsedTemplateParameters( + ArrayRef 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 a72c95d6d77cf..bfce05135e058 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -629,9 +629,14 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Inst.InstantiationRange = InstantiationRange; Inst.InConstraintSubstitution = Inst.Kind == CodeSynthesisContext::ConstraintSubstitution; - if (!SemaRef.CodeSynthesisContexts.empty()) + 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); @@ -1387,7 +1392,8 @@ getPackSubstitutedTemplateArgument(Sema &S, TemplateArgument Arg) { // Template Instantiation for Types //===----------------------------------------------------------------------===/ namespace { - class TemplateInstantiator : public TreeTransform { + +class TemplateInstantiator : public TreeTransform { const MultiLevelTemplateArgumentList &TemplateArgs; SourceLocation Loc; DeclarationName Entity; @@ -1399,13 +1405,15 @@ namespace { // Whether an incomplete substituion should be treated as an error. bool BailOutOnIncomplete; - private: + bool PreserveArgumentPacks = false; + bool BuildPackExpansionTypes = true; + // 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 inherited; TemplateInstantiator(Sema &SemaRef, @@ -1415,12 +1423,19 @@ 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, + SourceLocation Loc, + const MultiLevelTemplateArgumentList &TemplateArgs, + bool BuildPackExpansionTypes) + : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), + BailOutOnIncomplete(false), PreserveArgumentPacks(true), + BuildPackExpansionTypes(BuildPackExpansionTypes) {} + + void setEvaluateConstraints(bool B) { EvaluateConstraints = B; } + bool getEvaluateConstraints() { return EvaluateConstraints; } /// Determine whether the given type \p T has already been /// transformed. @@ -1463,7 +1478,8 @@ namespace { bool &ShouldExpand, bool &RetainExpansion, UnsignedOrNone &NumExpansions) { if (SemaRef.CurrentInstantiationScope && - SemaRef.inConstraintSubstitution()) { + (SemaRef.inConstraintSubstitution() || + SemaRef.inParameterMappingSubstitution())) { for (UnexpandedParameterPack ParmPack : Unexpanded) { NamedDecl *VD = ParmPack.first.dyn_cast(); if (auto *PVD = dyn_cast_if_present(VD); @@ -1484,10 +1500,10 @@ namespace { TemplateArgument ForgetPartiallySubstitutedPack() { TemplateArgument Result; - if (NamedDecl *PartialPack - = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){ - MultiLevelTemplateArgumentList &TemplateArgs - = const_cast(this->TemplateArgs); + if (NamedDecl *PartialPack = + SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()) { + MultiLevelTemplateArgumentList &TemplateArgs = + const_cast(this->TemplateArgs); unsigned Depth, Index; std::tie(Depth, Index) = getDepthAndIndex(PartialPack); if (TemplateArgs.hasTemplateArgument(Depth, Index)) { @@ -1507,10 +1523,10 @@ namespace { if (Arg.isNull()) return; - if (NamedDecl *PartialPack - = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){ - MultiLevelTemplateArgumentList &TemplateArgs - = const_cast(this->TemplateArgs); + if (NamedDecl *PartialPack = + SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()) { + MultiLevelTemplateArgumentList &TemplateArgs = + const_cast(this->TemplateArgs); unsigned Depth, Index; std::tie(Depth, Index) = getDepthAndIndex(PartialPack); TemplateArgs.setArgument(Depth, Index, Arg); @@ -1527,9 +1543,9 @@ namespace { std::move(New); return Old; } + void RememberSubstitution(MultiLevelTemplateArgumentList Old) { - const_cast(this->TemplateArgs) = - std::move(Old); + const_cast(this->TemplateArgs) = Old; } TemplateArgument @@ -1608,8 +1624,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 @@ -1617,8 +1632,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(), @@ -1645,8 +1667,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,6 +1736,24 @@ namespace { return inherited::TransformTemplateArgument(Input, Output, Uneval); } + // This has to be here to allow its overload. + ExprResult RebuildPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc, + UnsignedOrNone NumExpansions) { + return inherited::RebuildPackExpansion(Pattern, EllipsisLoc, + NumExpansions); + } + + TemplateArgumentLoc RebuildPackExpansion(TemplateArgumentLoc Pattern, + SourceLocation EllipsisLoc, + UnsignedOrNone NumExpansions) { + // We don't rewrite a PackExpansion type when we want to normalize a + // CXXFoldExpr constraint. We'll expand it when evaluating the constraint. + if (!PreserveArgumentPacks || BuildPackExpansionTypes) + return inherited::RebuildPackExpansion(Pattern, EllipsisLoc, + NumExpansions); + return Pattern; + } + using TreeTransform::TransformTemplateSpecializationType; QualType TransformTemplateSpecializationType(TypeLocBuilder &TLB, @@ -1751,7 +1791,7 @@ namespace { return inherited::ComputeSizeOfPackExprWithoutSubstitution(PackArgs); } - template + template QualType TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, CXXRecordDecl *ThisContext, @@ -1770,10 +1810,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) @@ -1910,13 +1952,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); + TemplateDeclInstantiator DeclInstantiator(getSema(), + /* DeclContext *Owner */ Owner, + TemplateArgs); DeclInstantiator.setEvaluateConstraints(EvaluateConstraints); return DeclInstantiator.SubstTemplateParams(OrigTPL); } @@ -1934,13 +1978,13 @@ namespace { SmallVectorImpl &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()) @@ -1990,7 +2034,8 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { if (ParmVarDecl *PVD = dyn_cast(D); PVD && SemaRef.CurrentInstantiationScope && - SemaRef.inConstraintSubstitution() && + (SemaRef.inConstraintSubstitution() || + SemaRef.inParameterMappingSubstitution()) && maybeInstantiateFunctionParameterToScope(PVD)) return nullptr; @@ -2221,6 +2266,15 @@ 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() || + SemaRef.inConstraintSubstitution()); + ExprResult Expr = SemaRef.BuildExpressionFromNonTypeTemplateArgument( + Arg, E->getLocation()); + if (Expr.isInvalid()) + return E; + Arg = TemplateArgument(Expr.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 @@ -2926,18 +2980,30 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) { concepts::NestedRequirement * TemplateInstantiator::TransformNestedRequirement( concepts::NestedRequirement *Req) { - if (!Req->isDependent() && !AlwaysRebuild()) - return Req; + + ASTContext &C = SemaRef.Context; + + Expr *Constraint = Req->getConstraintExpr(); + ExprResult TransConstraint = Constraint; + ConstraintSatisfaction Satisfaction; + + auto NestedReqWithDiag = [&C, this](Expr *E, + ConstraintSatisfaction Satisfaction) { + Satisfaction.IsSatisfied = false; + SmallString<128> Entity; + llvm::raw_svector_ostream OS(Entity); + E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); + return new (C) concepts::NestedRequirement( + SemaRef.Context, C.backupStr(Entity), std::move(Satisfaction)); + }; + if (Req->hasInvalidConstraint()) { if (AlwaysRebuild()) return RebuildNestedRequirement(Req->getInvalidConstraintEntity(), Req->getConstraintSatisfaction()); return Req; } - Sema::InstantiatingTemplate ReqInst(SemaRef, - Req->getConstraintExpr()->getBeginLoc(), Req, - Sema::InstantiatingTemplate::ConstraintsCheck{}, - Req->getConstraintExpr()->getSourceRange()); + if (!getEvaluateConstraints()) { ExprResult TransConstraint = TransformExpr(Req->getConstraintExpr()); if (TransConstraint.isInvalid() || !TransConstraint.get()) @@ -2950,45 +3016,45 @@ TemplateInstantiator::TransformNestedRequirement( SemaRef.Context, TransConstraint.get(), Satisfaction); } - ExprResult TransConstraint; - ConstraintSatisfaction Satisfaction; - TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc()); + bool Success; + Expr *NewConstraint; + 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, + Sema::InstantiatingTemplate::ConstraintsCheck(), + Constraint->getSourceRange()); + if (ConstrInst.isInvalid()) return nullptr; - llvm::SmallVector 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, + /*TopLevelConceptId=*/nullptr, &NewConstraint); + + 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); + + if (!Success || Satisfaction.HasSubstitutionFailure()) + return NestedReqWithDiag(Constraint, Satisfaction); + + // FIXME: const correctness + // MLTAL might be dependent. + if (!NewConstraint) { + if (!Satisfaction.IsSatisfied) + return NestedReqWithDiag(Constraint, Satisfaction); + + NewConstraint = Constraint; } - return new (C) - concepts::NestedRequirement(C, TransConstraint.get(), Satisfaction); + return new (C) concepts::NestedRequirement(C, NewConstraint, Satisfaction); } TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, @@ -3245,7 +3311,7 @@ bool Sema::SubstTypeConstraint( const ASTTemplateArgumentListInfo *TemplArgInfo = TC->getTemplateArgsAsWritten(); - if (!EvaluateConstraints) { + if (!EvaluateConstraints && !inParameterMappingSubstitution()) { UnsignedOrNone Index = TC->getArgPackSubstIndex(); if (!Index) Index = SemaRef.ArgPackSubstIndex; @@ -4545,6 +4611,16 @@ bool Sema::SubstTemplateArguments( return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out); } +bool Sema::SubstTemplateArgumentsInParameterMapping( + ArrayRef Args, SourceLocation BaseLoc, + const MultiLevelTemplateArgumentList &TemplateArgs, + TemplateArgumentListInfo &Out, bool BuildPackExpansionTypes) { + TemplateInstantiator Instantiator( + TemplateInstantiator::ForParameterMappingSubstitution, *this, BaseLoc, + TemplateArgs, BuildPackExpansionTypes); + return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out); +} + ExprResult Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) { if (!E) diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 242ffb09af006..129f362fbd263 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" @@ -47,8 +48,11 @@ #include "clang/Sema/SemaSYCL.h" #include "clang/Sema/Template.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/Support/ErrorHandling.h" #include +#include #include using namespace llvm::omp; @@ -352,6 +356,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 @@ -3714,10 +3720,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, @@ -5102,9 +5104,13 @@ bool TreeTransform::TransformTemplateArguments( typedef TemplateArgumentLocInventIterator PackLocIterator; + + TemplateArgumentListInfo *PackOutput = &Outputs; + TemplateArgumentListInfo 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; @@ -5171,7 +5177,6 @@ bool TreeTransform::TransformTemplateArguments( } return false; - } // FIXME: Find ways to reduce code duplication for pack expansions. @@ -6239,7 +6244,7 @@ ParmVarDecl *TreeTransform::TransformFunctionTypeParam( /* DefArg */ nullptr); newParm->setScopeInfo(OldParm->getFunctionScopeDepth(), OldParm->getFunctionScopeIndex() + indexAdjustment); - transformedLocalDecl(OldParm, {newParm}); + getDerived().transformedLocalDecl(OldParm, {newParm}); return newParm; } @@ -7074,11 +7079,11 @@ QualType TreeTransform::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(); @@ -7093,7 +7098,7 @@ QualType TreeTransform::TransformUnaryTransformType( UnaryTransformTypeLoc NewTL = TLB.push(Result); NewTL.setKWLoc(TL.getKWLoc()); NewTL.setParensRange(TL.getParensRange()); - NewTL.setUnderlyingTInfo(TL.getUnderlyingTInfo()); + NewTL.setUnderlyingTInfo(NewBaseTSI); return Result; } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index cf32d4f56b7c2..5456e73956659 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2424,7 +2424,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl( VisitDecl(D); llvm::SmallVector 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/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 213c2c2148f64..aec86e0986b77 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -807,15 +807,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 21c04ddbc2c7a..9408580fd7a8a 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" @@ -482,14 +483,20 @@ addConstraintSatisfaction(ASTRecordWriter &Record, if (!Satisfaction.IsSatisfied) { Record.push_back(Satisfaction.NumRecords); for (const auto &DetailRecord : Satisfaction) { - auto *E = dyn_cast(DetailRecord); - Record.push_back(/* IsDiagnostic */ E == nullptr); - if (E) - Record.AddStmt(E); - else { - auto *Diag = cast *>(DetailRecord); + if (auto *Diag = + dyn_cast *>(DetailRecord)) { + Record.push_back(/*Kind=*/0); Record.AddSourceLocation(Diag->first); Record.AddString(Diag->second); + continue; + } + if (auto *E = dyn_cast(DetailRecord)) { + Record.push_back(/*Kind=*/1); + Record.AddStmt(const_cast(E)); + } else { + Record.push_back(/*Kind=*/2); + auto *CR = cast(DetailRecord); + Record.AddConceptReference(CR); } } } 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 {{.*}} 'bool' Concept {{.*}} 'binary_concept' // CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} 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 {{.*}} 'bool' // CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} 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 Foo(R); diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp index 781fb9f28cb8d..9a3adbcb534e8 100644 --- a/clang/test/AST/ast-dump-ctad-alias.cpp +++ b/clang/test/AST/ast-dump-ctad-alias.cpp @@ -185,17 +185,18 @@ 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 '>' -// CHECK-NEXT: | | | | `-TemplateArgument type 'GH124715::Packs' -// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'GH124715::Packs' 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 '>' +// CHECK-NEXT: | | | | `-TemplateArgument type 'Packs' +// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'Packs' dependent +// CHECK-NEXT: | | | | |-name: 'Packs':'GH124715::Packs' qualified // CHECK-NEXT: | | | | | `-ClassTemplateDecl {{.*}} Packs -// CHECK-NEXT: | | | | `-TemplateArgument pack '' -// 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 5c2948f67d0ee..0e0fc735c6843 100644 --- a/clang/test/CXX/drs/cwg25xx.cpp +++ b/clang/test/CXX/drs/cwg25xx.cpp @@ -243,19 +243,20 @@ 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 - concept ErrorRequires = requires (ErrorRequires auto x) { + concept ErrorRequires = requires (ErrorRequires auto x) { // #cwg2565-expr // since-cxx20-error@-1 {{a concept definition cannot refer to itself}} // since-cxx20-note@-2 {{declared here}} // since-cxx20-error@-3 {{'auto' not allowed in requires expression parameter}} x; }; static_assert(ErrorRequires); - // since-cxx20-error@-1 {{static assertion failed}} - // since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // since-cxx20-error@-1 {{static assertion failed}} \ + // since-cxx20-note@-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 concept NestedErrorInRequires = requires (T x) { // #cwg2565-NEIR - requires requires (NestedErrorInRequires auto y) { + requires requires (NestedErrorInRequires auto y) { // #cwg2565-NEIR-inner // since-cxx20-error@-1 {{a concept definition cannot refer to itself}} // since-cxx20-note@#cwg2565-NEIR {{declared here}} // since-cxx20-error@-3 {{'auto' not allowed in requires expression parameter}} @@ -263,8 +264,9 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07 }; }; static_assert(NestedErrorInRequires); - // since-cxx20-error@-1 {{static assertion failed}} - // since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // since-cxx20-error@-1 {{static assertion failed}} \ + // since-cxx20-note@-1 {{because 'int' does not satisfy 'NestedErrorInRequires'}} \ + // 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.id/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp index 28b5d0adcf054..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 @@ -140,7 +140,8 @@ concept C7 = sizeof(T) == 1 || sizeof( ::type) == 1; static_assert(!C6); -static_assert(!C6); // expected-note{{while checking the satisfaction of concept 'C6' requested here}} +static_assert(!C6); +// expected-note@-1 {{while checking the satisfaction of concept 'C6' requested here}} static_assert(C7); static_assert(!C7); // expected-note{{while checking the satisfaction of concept 'C7' requested here}} 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..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 @@ -35,14 +35,14 @@ using r2i2 = r2; // expected-error{{constraints not satisfied for class templ using r2i3 = r2; using r2i4 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}} -template 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 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; using r3i2 = r3; using r3i3 = r3; using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}} -using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}} +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}} // Non-dependent expressions @@ -89,7 +89,7 @@ template concept Large = sizeof(typename remove_reference::type) >= 4; // expected-note@-1{{because 'sizeof(typename remove_reference::type) >= 4' (2 >= 4) evaluated to false}} -template requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large':}} +template requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large'}} struct r7 {}; using r7i1 = r7; @@ -149,7 +149,7 @@ namespace std_example { template constexpr bool is_same_v = true; template concept same_as = is_same_v; - // expected-note@-1 {{because 'is_same_v' evaluated to false}} + // expected-note@-1 {{because 'is_same_v' evaluated to false}} static_assert(C1); static_assert(C1); @@ -160,7 +160,7 @@ namespace std_example { template concept C2 = requires(T x) { {*x} -> same_as; - // expected-note@-1{{because type constraint 'same_as' was not satisfied:}} + // expected-note@-1{{because 'same_as' evaluated to false}} // expected-note@-2{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}} }; @@ -173,9 +173,9 @@ namespace std_example { int operator *() { return 0; } }; static_assert(C2); - template struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}} + template struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'T2' does not satisfy 'C2'}} using c2c1 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}} - using c2c2 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}} + using c2c2 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = T2]}} template 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 033ae349a02e5..70a96bed05867 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@-1{{while checking the satisfaction of nested requirement requested here}} - // expected-note@-2{{in instantiation of requirement here}} - // expected-note@-3{{while checking the satisfaction of nested requirement requested here}} - // expected-note@-6{{while substituting template arguments into constraint expression here}} - // expected-note@-5{{function parameter 'a' with unknown value cannot be used in a constant expression}} - // expected-note@-8{{declared here}} + // expected-note@-2{{while checking the satisfaction of nested requirement requested here}} + // expected-note@-5{{while substituting template arguments into constraint expression here}} + // expected-note@-4{{function parameter 'a' with unknown value cannot be used in a constant expression}} + // expected-note@-7{{declared here}} }; static_assert(C2); // expected-error{{static assertion failed}} // expected-note@-1{{while checking the satisfaction of concept 'C2' requested here}} @@ -84,31 +83,26 @@ static_assert(Pipes); static_assert(Pipes); static_assert(Amps1); -static_assert(!Amps1); +static_assert(Amps1); static_assert(Amps2); -static_assert(!Amps2); +static_assert(Amps2); template -void foo1() requires requires (T x) { // #foo1 +void foo1() requires requires (T x) { requires - True // #foo1Value + True && True; } {} template void fooPipes() requires Pipes {} -template void fooAmps1() requires Amps1 {} // #fooAmps1 +template void fooAmps1() requires Amps1 {} void foo() { foo1(); - foo1(); // expected-error {{no matching function for call to 'foo1'}} - // expected-note@#foo1Value {{because 'True && True' would be invalid: member reference base type 'int' is not a structure or union}} - // expected-note@#foo1 {{candidate template ignored: constraints not satisfied [with T = int]}} + foo1(); fooPipes(); fooPipes(); fooAmps1(); - fooAmps1(); // expected-error {{no matching function for call to 'fooAmps1'}} - // expected-note@#fooAmps1 {{candidate template ignored: constraints not satisfied [with T = int]}} - // expected-note@#fooAmps1 {{because 'int' does not satisfy 'Amps1'}} - // expected-note@#Amps1 {{because 'True && True && !False' would be invalid: member reference base type 'int' is not a structure or union}} + fooAmps1(); } template @@ -158,15 +152,16 @@ 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::value' evaluated to false}} + // expected-note@#bar {{because 'X::value' evaluated to false}} bar(); + // expected-error@-1 {{no matching function for call to 'bar'}} \ // expected-note@-1 {{while checking constraint satisfaction for template 'bar' required here}} \ - // expected-note@-1 {{while substituting deduced template arguments into function template 'bar' [with T = int]}} + // expected-note@-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/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; // expected-error{{constraints not satisfied for class template requires requires { sizeof(T); } // expected-note@-1{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} -// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}} +// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}} struct r3 {}; using r3i1 = r3; using r3i2 = r3; using r3i3 = r3; using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}} -using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}} +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}} template 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 && C2 && C3); template struct C1_check {}; // expected-note@-1 {{because 'int' does not satisfy 'C1'}} - // expected-note@-2 {{because 'std_example::has_type' does not satisfy 'C1'}} + // expected-note@-2 {{because 'has_type' does not satisfy 'C1'}} template struct C2_check {}; - // expected-note@-1 {{because 'std_example::has_inner' does not satisfy 'C2'}} + // expected-note@-1 {{because 'has_inner' does not satisfy 'C2'}} template struct C3_check {}; // expected-note@-1 {{because 'void' does not satisfy 'C3'}} using c1 = C1_check; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}} - using c2 = C1_check; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}} - using c3 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}} + using c2 = C1_check; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = has_type]}} + using c3 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = has_inner]}} using c4 = C3_check; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}} } @@ -199,10 +199,10 @@ template concept C = requires { requires requires { T::a; }; }; // expected-note@-1 {{because 'T::a' would be invalid: no member named 'a' in 'PR48656::T1'}} template struct A {}; -// expected-note@-1 {{because 'PR48656::T1' does not satisfy 'C'}} +// expected-note@-1 {{because 'T1' does not satisfy 'C'}} struct T1 {}; -template struct A; // expected-error {{constraints not satisfied for class template 'A' [with $0 = ]}} +template struct A; // expected-error {{constraints not satisfied for class template 'A' [with $0 = ]}} struct T2 { static constexpr bool a = false; }; template struct A; 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..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,9 +28,8 @@ template requires requires { requires S{}; // expected-error@-1{{atomic constraint must be of type 'bool' (found 'S')}} // expected-note@-2{{while checking the satisfaction}} - // expected-note@-3{{in instantiation of requirement}} - // expected-note@-4{{while checking the satisfaction}} - // expected-note@-6{{while substituting template arguments}} + // expected-note@-3{{while checking the satisfaction of nested requirement}} + // expected-note@-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..35796106da278 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,21 +1,31 @@ // RUN: %clang_cc1 -std=c++2a -x c++ -verify %s +// FIXME: RUN: %clang_cc1 -std=c++2c -x c++ -verify %s template concept True = true; -template concept Foo = True; -template concept Bar = Foo; -template requires Bar struct S { }; -template requires Bar && true struct S { }; +template concept Foo = True; // #Foo +template concept Bar = Foo; // #Bar +template requires Bar struct S { }; // #S +template requires Bar && true struct S { }; // #SpecS +// expected-error@-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@#SpecS {{while substituting into concept arguments here}} +// expected-note@#S {{while substituting into concept arguments here}} +// expected-note@#Bar 2{{while substituting into concept arguments here}} +// expected-note@#S {{template is declared here}} + + template concept True2 = sizeof(T) >= 0; -template concept Foo2 = True2; -// expected-error@-1{{'type name' declared as a pointer to a reference of type 'type-parameter-0-0 &'}} -template concept Bar2 = Foo2; -// expected-note@-1{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}} -template requires Bar2 struct S2 { }; +template concept Foo2 = True2; // #Foo2 + +template concept Bar2 = Foo2; // #Bar2 +// expected-note@-1 3{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}} +template requires Bar2 struct S2 { }; // #SpecS2_1 // expected-note@-1{{template is declared here}} -template requires Bar2 && true struct S2 { }; +template requires Bar2 && true struct S2 { }; // #SpecS2_2 // expected-error@-1{{class template partial specialization is not more specialized than the primary template}} -// expected-note@-2{{while calculating associated constraint of template 'S2' here}} +// expected-error@#Foo2{{'type name' declared as a pointer to a reference of type 'T &'}} + namespace type_pack { template @@ -71,16 +81,31 @@ 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 +template // #S3_Header requires true struct S3; // expected-note {{template is declared here}} template -requires true struct S3; // expected-error {{class template partial specialization is not more specialized than the primary template}} +requires true struct S3; +// expected-error@-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@#SpecS2_1 {{while substituting into concept arguments here}} +// expected-note@#SpecS2_2 {{while substituting into concept arguments here}} +// expected-note@#S3_Header {{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 -requires true struct S4; // expected-note {{template is declared here}} +template // #S4_Header +requires true struct S4; // #S4 template -requires true struct S4; // expected-error {{class template partial specialization is not more specialized than the primary template}} +requires true struct S4; // #S4-spec +// expected-error@-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@#S4_Header {{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 struct Y { @@ -96,7 +121,7 @@ template requires C1 && C2 void t1() = delete; // expected-note { template void t1(); void t1() { t1(); } // expected-error {{call to deleted function 't1'}} -template requires C1 void t2() {}; // expected-note 2 {{candidate function}} +template requires C1 void t2() {}; // expected-note 2 {{candidate function}} template requires C2 void t2() {}; // expected-note 2 {{candidate function}} template void t2(); // expected-error {{partial ordering for explicit instantiation of 't2' is ambiguous}} void t2() { t2(); } // expected-error {{call to 't2' is ambiguous}} 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; using f2 = F; // expected-error {{constraints not satisfied for alias template 'F' [with T = long]}} template -concept OneOf = (is_same_v || ...); -// expected-note@-1 2{{because 'is_same_v' evaluated to false}} -// expected-note@-2 2{{and 'is_same_v' evaluated to false}} -// expected-note@-3 {{because 'is_same_v' evaluated to false}} -// expected-note@-4 {{and 'is_same_v' evaluated to false}} -// expected-note@-5 {{and 'is_same_v' evaluated to false}} -// expected-note@-6 3{{because 'is_same_v' evaluated to false}} -// expected-note@-7 3{{and 'is_same_v' evaluated to false}} -// expected-note@-8 2{{because 'is_same_v' evaluated to false}} -// expected-note@-9 2{{and 'is_same_v' evaluated to false}} +concept OneOf = (is_same_v || ...); // #OneOf +// expected-note@#OneOf 2{{because 'is_same_v' evaluated to false}} +// expected-note@#OneOf 2{{and 'is_same_v' evaluated to false}} +// expected-note@#OneOf {{because 'is_same_v' evaluated to false}} +// expected-note@#OneOf {{and 'is_same_v' evaluated to false}} +// expected-note@#OneOf {{and 'is_same_v' evaluated to false}} +// expected-note@#OneOf 3{{because 'is_same_v' evaluated to false}} +// expected-note@#OneOf 3{{and 'is_same_v' evaluated to false}} +// expected-note@#OneOf {{because 'is_same_v' evaluated to false}} +// expected-note@#OneOf {{because 'is_same_v' evaluated to false}} +// expected-note@#OneOf {{and 'is_same_v' evaluated to false}} +// expected-note@#OneOf {{and 'is_same_v' evaluated to false}} template T, OneOf U> // expected-note@-1 2{{because 'OneOf' 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; // expected-error@-1 {{constraints not satisfied for alias template 'I' [with x = nullptr]}} diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp index 99a82d96d321b..ce862666aa48f 100644 --- a/clang/test/SemaCXX/cxx23-assume.cpp +++ b/clang/test/SemaCXX/cxx23-assume.cpp @@ -127,13 +127,12 @@ struct F { template constexpr int f5() requires C { return 1; } // expected-note {{while checking the satisfaction}} - // expected-note@-1 {{while substituting template arguments}} - // expected-note@-2 {{candidate template ignored}} + // expected-note@-1 {{candidate template ignored}} template -constexpr int f5() requires (!C) { return 2; } // expected-note 4 {{while checking the satisfaction}} - // expected-note@-1 4 {{while substituting template arguments}} - // expected-note@-2 {{candidate template ignored}} +constexpr int f5() requires (!C) { return 2; } // expected-note 4 {{while checking the satisfaction}} \ + // expected-note 4 {{while substituting template arguments}} \ + // expected-note {{candidate template ignored}} static_assert(f5() == 1); static_assert(f5() == 1); // expected-note 3 {{while checking constraint satisfaction}} 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@-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, 0); (&A::e)(a, a); // expected-error@-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, 0); @@ -1273,12 +1273,12 @@ void f() { (&A::f)(a); // expected-error@-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@-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-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp index 4220486d3aed3..137f46ee3dc01 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 concept A = true; -template concept C = A && true; +template concept A = (T(), true); +template concept C = A && true; // #C template concept D = A && __is_same(T, int); @@ -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 requires (A || ... || true) -constexpr int j(T...) { return 0; }; -template requires (C && ... && true) -constexpr int j(T...) { return 1; }; +template requires (A || ... || true) constexpr int j(T...) { return 0; }; // #j1 +template requires (C && ... && true) constexpr int j(T...) { return 1; }; // #j2 static_assert(j(0) == 1); +// expected-error@-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}} + + static_assert(j() == 1); +// expected-error@-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}} @@ -107,7 +117,7 @@ void test() { } namespace substitution { - struct S { +struct S { using type = int; }; @@ -144,51 +154,69 @@ consteval int Or3() requires (C || ... || C) static_assert(And1<>() == 1); static_assert(And1() == 1); static_assert(And1() == 1); +// FIXME: The diagnostics are not so great static_assert(And1() == 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: constraints not satisfied [with T = ]}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And1() == 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: constraints not satisfied [with T = ]}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And1() == 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: constraints not satisfied [with T = ]}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And2() == 2); static_assert(And2() == 2); -static_assert(And2() == 2); +static_assert(And2() == 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() == 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: constraints not satisfied [with T = S, U = ]}} \ + // expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And2() == 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: constraints not satisfied [with T = int, U = ]}} + // expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And2() == 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: constraints not satisfied [with T = int, U = ]}} + // expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And3() == 3); static_assert(And3() == 3); static_assert(And3() == 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: 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() == 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: 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() == 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: constraints not satisfied [with T = S, U = ]}} + // expected-note@#and3 {{because 'typename U::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + static_assert(And3() == 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: 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(Or1<>() == 1); // expected-error {{no matching function for call to 'Or1'}} @@ -198,25 +226,26 @@ static_assert(Or1() == 1); static_assert(Or1() == 1); static_assert(Or1() == 1); static_assert(Or1() == 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}} + // expected-note@#or1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(Or2() == 2); static_assert(Or2() == 2); static_assert(Or2() == 2); static_assert(Or2() == 2); static_assert(Or2() == 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: 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() == 3); static_assert(Or3() == 3); static_assert(Or3() == 3); static_assert(Or3() == 3); static_assert(Or3() == 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}} + // expected-note@#or3 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} } namespace bool_conversion_break { @@ -226,7 +255,7 @@ struct Thingy { static constexpr int compare(const Thingy&) {return 1;} }; template -void f(A *, A *) // expected-note {{candidate template ignored: failed template argument deduction}} +void f(A *, A *) // 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() { @@ -269,9 +298,7 @@ struct S { static_assert(S::f() == 2); -static_assert(S::g() == 2); // expected-error {{call to 'g' is ambiguous}} - // expected-note@#nested-ambiguous-g1 {{candidate}} - // expected-note@#nested-ambiguous-g2 {{candidate}} +static_assert(S::g() == 2); } @@ -384,3 +411,98 @@ struct LazyLitMatrix, init> { } } + +namespace GH135190 { +template +concept A = __is_same_as(T, int) || __is_same_as(T, double) ; + +template +concept B = A && __is_same_as(T, double); + +template +requires(A && ...) +constexpr int g() { + return 1; +} + +template +requires(B && ...) +constexpr int g() { + return 2; +} + +static_assert(g() == 2); + + +template +concept all_A = (A && ...); + +template +concept all_B = (B && ...); + +template +requires all_A +constexpr int h() { + return 1; +} + +template +requires all_B +constexpr int h() { + return 2; +} + +static_assert(h() == 2); +} + + +namespace parameter_mapping_regressions { + +namespace case1 { +namespace std { +template +constexpr bool is_constructible_v = __is_constructible(_Tp, _Args...); +template +concept constructible_from = is_constructible_v<_Tp, _Args...>; +template +concept default_initializable = true; +template using iterator_t = int; +template +concept view = constructible_from<_Tp, _Tp>; +template + requires(view<_Views> && ...) +class zip_transform_view; +} // namespace std +struct IterDefaultCtrView {}; +template +using Iter = std::iterator_t>; +static_assert( + std::default_initializable>); + +} + +namespace case2 { + +template +constexpr bool False = false; + +template +concept __zip_all_random_access = (False<_Views> && ...); +// expected-note@-1 {{evaluated to false}} + +template +struct zip_view { + void f() requires __zip_all_random_access<_Views...>{}; + // expected-note@-1 {{because 'int' does not satisfy}} +}; + +zip_view test_v; +static_assert(!__zip_all_random_access); + +void test() { + test_v.f(); // expected-error {{invalid reference to function 'f'}} +} + +} + +} 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