From ed73236d4f44d0fca16cfd43786614e5b6e10814 Mon Sep 17 00:00:00 2001 From: Corentin Jabot Date: Thu, 2 Oct 2025 15:52:15 +0200 Subject: [PATCH] Revert "[Clang] Normalize constraints before checking for satisfaction (#141776)" This reverts commit 9583b399d85cacdfa0a41f798ab44abaa3981bbf. --- clang/docs/InternalsManual.rst | 61 - clang/docs/ReleaseNotes.rst | 4 - clang/include/clang/AST/ASTConcept.h | 33 +- clang/include/clang/AST/ASTContext.h | 1 + clang/include/clang/Sema/Sema.h | 105 +- clang/include/clang/Sema/SemaConcept.h | 434 +--- clang/include/clang/Sema/Template.h | 22 +- clang/lib/AST/ASTConcept.cpp | 31 +- clang/lib/AST/ASTImporter.cpp | 12 +- clang/lib/Sema/SemaConcept.cpp | 2012 ++++++----------- clang/lib/Sema/SemaDeclCXX.cpp | 16 +- clang/lib/Sema/SemaExprCXX.cpp | 16 +- clang/lib/Sema/SemaOverload.cpp | 3 +- clang/lib/Sema/SemaTemplate.cpp | 93 +- clang/lib/Sema/SemaTemplateDeduction.cpp | 51 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 169 +- clang/lib/Sema/TreeTransform.h | 19 +- clang/lib/Serialization/ASTReaderDecl.cpp | 2 +- clang/lib/Serialization/ASTReaderStmt.cpp | 14 +- clang/lib/Serialization/ASTWriterStmt.cpp | 18 +- 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 | 4 +- ...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 | 71 +- 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 | 97 +- clang/test/SemaTemplate/pr52970.cpp | 2 +- .../cpp17_iterator_concepts.verify.cpp | 4 +- 50 files changed, 1197 insertions(+), 2638 deletions(-) diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index c677ddfa5ecc1..bd742273f4ed5 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -2859,67 +2859,6 @@ This library is called by the :ref:`Parser library ` during parsing to do semantic analysis of the input. For valid programs, Sema builds an AST for parsed constructs. - -Concept Satisfaction Checking and Subsumption ---------------------------------------------- - -As per the C++ standard, constraints are `normalized `_ -and the normal form is used both for subsumption, and constraint checking. -Both depend on a parameter mapping that substitutes lazily. In particular, -we should not substitute in unused arguments. - -Clang follows the order of operations prescribed by the standard. - -Normalization happens prior to satisfaction and subsumption -and is handled by ``NormalizedConstraint``. - -Clang preserves in the normalized form intermediate concept-ids -(``ConceptIdConstraint``) This is used for diagnostics only and no substitution -happens in a ConceptIdConstraint if its expression is satisfied. - -The normal form of the associated constraints of a declaration is cached in -Sema::NormalizationCache such that it is only computed once. - -A ``NormalizedConstraint`` is a recursive data structure, where each node -contains a parameter mapping, represented by the indexes of all parameter -being used. - -Checking satisfaction is done by ``ConstraintSatisfactionChecker``, recursively -walking ``NormalizedConstraint``. At each level, we substitute the outermost -level of the template arguments referenced in the parameter mapping of a -normalized expression (``MultiLevelTemplateArgumentList``). - -For the following example, - -.. code-block:: c++ - - template - concept A = __is_same(T, int); - - template - concept B = A && __is_same(U, int); - -The normal form of B is - -.. code-block:: c++ - - __is_same(T, int) /*T->U, innermost level*/ - && __is_same(U, int) {U->U} /*T->U, outermost level*/ - -After substitution in the mapping, we substitute in the constraint expression -using that copy of the ``MultiLevelTemplateArgumentList``, and then evaluate it. - -Because this is expensive, it is cached in -``UnsubstitutedConstraintSatisfactionCache``. - -Any error during satisfaction is recorded in ``ConstraintSatisfaction``. -for nested requirements, ``ConstraintSatisfaction`` is stored (including -diagnostics) in the AST, which is something we might want to improve. - -When an atomic constraint is not satified, we try to substitute into any -enclosing concept-id using the same mechanism described above, for -diagnostics purpose, and inject that in the ``ConstraintSatisfaction``. - .. _CodeGen: The CodeGen Library diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a1e3a0c51d8e1..c6ee1e282a008 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -160,10 +160,6 @@ C++23 Feature Support C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ -- Clang now normalizes constraints before checking whether they are satisfied, as mandated by the standard. - As a result, Clang no longer incorrectly diagnoses substitution failures in template arguments only - used in concept-ids, and produces better diagnostics for satisfaction failure. (#GH61811) (#GH135190) - C++17 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index f362f24ebc72a..72da0059744f2 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -28,20 +28,10 @@ namespace clang { class ConceptDecl; class TemplateDecl; -class ConceptReference; class Expr; class NamedDecl; struct PrintingPolicy; -/// Unsatisfied constraint expressions if the template arguments could be -/// substituted into them, or a diagnostic if substitution resulted in -/// an invalid expression. -/// -using ConstraintSubstitutionDiagnostic = std::pair; -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 { @@ -58,13 +48,16 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode { ArrayRef TemplateArgs) : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {} + using SubstitutionDiagnostic = std::pair; + using Detail = llvm::PointerUnion; + bool IsSatisfied = false; bool ContainsErrors = false; /// \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); @@ -76,12 +69,19 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode { bool HasSubstitutionFailure() { for (const auto &Detail : Details) - if (Detail.dyn_cast()) + if (Detail.dyn_cast()) return true; return false; } }; +/// 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,10 +101,6 @@ struct ASTConstraintSatisfaction final : return getTrailingObjects() + NumRecords; } - ArrayRef records() const { - return {begin(), end()}; - } - ASTConstraintSatisfaction(const ASTContext &C, const ConstraintSatisfaction &Satisfaction); ASTConstraintSatisfaction(const ASTContext &C, @@ -286,11 +282,6 @@ class TypeConstraint { } }; -/// Insertion operator for diagnostics. This allows sending ConceptReferences'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 78220d4d8ff5b..12351e98e5a2b 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -3877,6 +3877,7 @@ typename clang::LazyGenerationalUpdatePtr::ValueType return new (Ctx) LazyData(Source, Value); return Value; } + template <> struct llvm::DenseMapInfo { static FoldingSetNodeID getEmptyKey() { return FoldingSetNodeID{}; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index bd3e042868299..f53aafdeb4f36 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -65,7 +65,6 @@ #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,9 +11694,8 @@ class Sema final : public SemaBase { ExprResult CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, const DeclarationNameInfo &ConceptNameInfo, - NamedDecl *FoundDecl, TemplateDecl *NamedConcept, - const TemplateArgumentListInfo *TemplateArgs, - bool DoCheckConstraintSatisfaction = true); + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs); void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc); void diagnoseMissingTemplateArguments(const CXXScopeSpec &SS, @@ -12027,13 +12025,6 @@ 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, @@ -12792,18 +12783,6 @@ class Sema final : public SemaBase { void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced, unsigned Depth, llvm::SmallBitVector &Used); - /// Mark which template parameters are named in a given expression. - /// - /// Unlike MarkUsedTemplateParameters, this excludes parameter that - /// are used but not directly named by an expression - i.e. it excludes - /// any template parameter that denotes the type of a referenced NTTP. - /// - /// \param Used a bit vector whose elements will be set to \c true - /// to indicate when the corresponding template parameter will be - /// deduced. - void MarkUsedTemplateParametersForSubsumptionParameterMapping( - const Expr *E, unsigned Depth, llvm::SmallBitVector &Used); - /// Mark which template parameters can be deduced from a given /// template argument list. /// @@ -12820,9 +12799,6 @@ 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) { @@ -13120,9 +13096,6 @@ class Sema final : public SemaBase { /// Whether we're substituting into constraints. bool InConstraintSubstitution; - /// Whether we're substituting into the parameter mapping of a constraint. - bool InParameterMappingSubstitution; - /// The point of instantiation or synthesis within the source code. SourceLocation PointOfInstantiation; @@ -13386,11 +13359,6 @@ 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. /// @@ -13852,12 +13820,6 @@ 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 @@ -14742,10 +14704,6 @@ 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 @@ -14770,12 +14728,44 @@ class Sema final : public SemaBase { /// \returns true if an error occurred and satisfaction could not be checked, /// false otherwise. bool CheckConstraintSatisfaction( - ConstrainedDeclOrNestedRequirement Entity, + const NamedDecl *Template, ArrayRef AssociatedConstraints, const MultiLevelTemplateArgumentList &TemplateArgLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction, - const ConceptReference *TopLevelConceptId = nullptr, - Expr **ConvertedExpr = nullptr); + 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); /// \brief Check whether the given non-dependent constraint expression is /// satisfied. Returns false and updates Satisfaction with the satisfaction @@ -14841,17 +14831,16 @@ class Sema final : public SemaBase { /// \param First whether this is the first time an unsatisfied constraint is /// diagnosed for this error. void DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction, - SourceLocation Loc = {}, bool First = true); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. void - DiagnoseUnsatisfiedConstraint(const ConceptSpecializationExpr *ConstraintExpr, + DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction, bool First = true); const NormalizedConstraint *getNormalizedAssociatedConstraints( - ConstrainedDeclOrNestedRequirement Entity, + const NamedDecl *ConstrainedDecl, ArrayRef AssociatedConstraints); /// \brief Check whether the given declaration's associated constraints are @@ -14876,15 +14865,6 @@ class Sema final : public SemaBase { const NamedDecl *D1, ArrayRef AC1, const NamedDecl *D2, ArrayRef AC2); - /// Cache the satisfaction of an atomic constraint. - /// The key is based on the unsubstituted expression and the parameter - /// mapping. This lets us not substituting the mapping more than once, - /// which is (very!) expensive. - /// FIXME: this should be private. - llvm::DenseMap - UnsubstitutedConstraintSatisfactionCache; - private: /// Caches pairs of template-like decls whose associated constraints were /// checked for subsumption and whether or not the first's constraints did in @@ -14895,11 +14875,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; - /// Cache whether the associated constraint of a declaration - /// is satisfied. llvm::ContextualFoldingSet SatisfactionCache; diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h index 51ca1e16331f5..648a9c51ae6c1 100644 --- a/clang/include/clang/Sema/SemaConcept.h +++ b/clang/include/clang/Sema/SemaConcept.h @@ -16,406 +16,130 @@ #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/Sema/Ownership.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLFunctionalExtras.h" -#include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallVector.h" #include #include namespace clang { class Sema; -class MultiLevelTemplateArgumentList; -/// \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 : unsigned char { - Atomic = 0, - ConceptId, - FoldExpanded, - Compound, - }; - - enum CompoundConstraintKind : unsigned char { - CCK_Conjunction, - CCK_Disjunction - }; - enum class FoldOperatorKind : unsigned char { And, Or }; - - using OccurenceList = llvm::SmallBitVector; - -protected: - using ExprOrConcept = - llvm::PointerUnion; - - struct AtomicConstraintBits { - // Kind is the first member of all union members, - // as we rely on their initial common sequence. - LLVM_PREFERRED_TYPE(ConstraintKind) - unsigned Kind : 5; - unsigned Placeholder : 1; - unsigned PackSubstitutionIndex : 26; - // Indexes, IndexesForSubsumption, and Args are part of the common initial - // sequences of constraints that do have a mapping. - - // Indexes of the parameters used in a constraint expression. - OccurenceList Indexes; - // Indexes of the parameters named directly in a constraint expression. - // FIXME: we should try to reduce the size of this struct? - OccurenceList IndexesForSubsumption; - - TemplateArgumentLoc *Args; - TemplateParameterList *ParamList; - ExprOrConcept ConstraintExpr; - const NamedDecl *ConstraintDecl; - }; - - struct FoldExpandedConstraintBits { - LLVM_PREFERRED_TYPE(ConstraintKind) - unsigned Kind : 5; - LLVM_PREFERRED_TYPE(FoldOperatorKind) - unsigned FoldOperator : 1; - unsigned Placeholder : 26; - OccurenceList Indexes; - OccurenceList IndexesForSubsumption; - TemplateArgumentLoc *Args; - TemplateParameterList *ParamList; - const Expr *Pattern; - const NamedDecl *ConstraintDecl; - NormalizedConstraint *Constraint; - }; - - struct ConceptIdBits : AtomicConstraintBits { - NormalizedConstraint *Sub; - - // Only used for parameter mapping. - const ConceptSpecializationExpr *CSE; - }; - - struct CompoundConstraintBits { - LLVM_PREFERRED_TYPE(ConstraintKind) - unsigned Kind : 5; - LLVM_PREFERRED_TYPE(CompoundConstraintKind) - unsigned CCK : 1; - NormalizedConstraint *LHS; - NormalizedConstraint *RHS; - }; - - union { - AtomicConstraintBits Atomic; - FoldExpandedConstraintBits FoldExpanded; - ConceptIdBits ConceptId; - CompoundConstraintBits 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=*/{}, - /*IndexesForSubsumption=*/{}, - /*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=*/{}, - /*IndexesForSubsumption=*/{}, - /*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=*/{}, - /*IndexesForSubsumption=*/{}, - /*Args=*/nullptr, /*ParamList=*/nullptr, ConceptId, - ConstraintDecl}, - SubConstraint, - CSE} {} - - NormalizedConstraint(NormalizedConstraint *LHS, CompoundConstraintKind CCK, - NormalizedConstraint *RHS) - : Compound{llvm::to_underlying(ConstraintKind::Compound), - llvm::to_underlying(CCK), LHS, RHS} {} - - bool hasParameterMapping() const { - // compound constraints do not have a mapping - // and Args is not part of their common initial sequence. - return getKind() != ConstraintKind::Compound && Atomic.Args != nullptr; - } - - const OccurenceList &mappingOccurenceList() const { - assert(hasParameterMapping() && "This constraint has no parameter mapping"); - return Atomic.Indexes; - } - - const OccurenceList &mappingOccurenceListForSubsumption() const { - assert(hasParameterMapping() && "This constraint has no parameter mapping"); - return Atomic.IndexesForSubsumption; - } +enum { ConstraintAlignment = 8 }; - llvm::MutableArrayRef getParameterMapping() const { - return {Atomic.Args, Atomic.Indexes.count()}; - } - - TemplateParameterList *getUsedTemplateParamList() const { - return Atomic.ParamList; - } +struct alignas(ConstraintAlignment) AtomicConstraint { + const Expr *ConstraintExpr; + const NamedDecl *ConstraintDecl; + std::optional> ParameterMapping; - void updateParameterMapping(OccurenceList Indexes, - OccurenceList IndexesForSubsumption, - llvm::MutableArrayRef Args, - TemplateParameterList *ParamList) { - assert(getKind() != ConstraintKind::Compound); - assert(Indexes.count() == Args.size()); - assert(IndexesForSubsumption.size() == Indexes.size()); - assert((Indexes | IndexesForSubsumption) == Indexes); - - Atomic.IndexesForSubsumption = std::move(IndexesForSubsumption); - Atomic.Indexes = std::move(Indexes); - Atomic.Args = Args.data(); - Atomic.ParamList = ParamList; - } + AtomicConstraint(const Expr *ConstraintExpr, const NamedDecl *ConstraintDecl) + : ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {}; bool hasMatchingParameterMapping(ASTContext &C, - const NormalizedConstraint &Other) const { - assert(getKind() != ConstraintKind::Compound); - - if (hasParameterMapping() != Other.hasParameterMapping()) + const AtomicConstraint &Other) const { + if (!ParameterMapping != !Other.ParameterMapping) return false; - if (!hasParameterMapping()) + if (!ParameterMapping) return true; - - llvm::ArrayRef ParameterMapping = - getParameterMapping(); - llvm::ArrayRef OtherParameterMapping = - Other.getParameterMapping(); - - const OccurenceList &Indexes = mappingOccurenceListForSubsumption(); - const OccurenceList &OtherIndexes = - Other.mappingOccurenceListForSubsumption(); - - if (ParameterMapping.size() != OtherParameterMapping.size()) + if (ParameterMapping->size() != Other.ParameterMapping->size()) return false; - for (unsigned I = 0, S = ParameterMapping.size(); I < S; ++I) { - if (Indexes[I] != OtherIndexes[I]) - return false; - if (!Indexes[I]) - continue; + + 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(OtherParameterMapping[I].getArgument()) + C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument()) .Profile(IDB, C); if (IDA != IDB) return false; } return true; } - -public: - ConstraintKind getKind() const { - return static_cast(Atomic.Kind); - } - - 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); }; -class CompoundConstraint : public NormalizedConstraint { - using NormalizedConstraint::NormalizedConstraint; +struct alignas(ConstraintAlignment) NormalizedConstraintPair; +struct alignas(ConstraintAlignment) FoldExpandedConstraint; -public: - static CompoundConstraint *Create(ASTContext &Ctx, NormalizedConstraint *LHS, - CompoundConstraintKind CCK, - NormalizedConstraint *RHS) { - return new (Ctx) CompoundConstraint(LHS, CCK, RHS); - } +/// \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 { + friend class Sema; - static CompoundConstraint *CreateConjunction(ASTContext &Ctx, - NormalizedConstraint *LHS, - NormalizedConstraint *RHS) { - return new (Ctx) CompoundConstraint(LHS, CCK_Conjunction, RHS); - } + enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; - const NormalizedConstraint &getLHS() const { return *Compound.LHS; } + using CompoundConstraint = llvm::PointerIntPair; - NormalizedConstraint &getLHS() { return *Compound.LHS; } + llvm::PointerUnion + Constraint; - const NormalizedConstraint &getRHS() const { return *Compound.RHS; } + NormalizedConstraint(AtomicConstraint *C): Constraint{C} { }; + NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {}; - NormalizedConstraint &getRHS() { return *Compound.RHS; } + NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, + NormalizedConstraint RHS, CompoundConstraintKind Kind); - CompoundConstraintKind getCompoundKind() const { - return static_cast(Compound.CCK); + NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other); + NormalizedConstraint(NormalizedConstraint &&Other): + Constraint(Other.Constraint) { + Other.Constraint = nullptr; } -}; - -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::mappingOccurenceListForSubsumption; - using NormalizedConstraint::updateParameterMapping; - - const NamedDecl *getConstraintDecl() const { return Atomic.ConstraintDecl; } - - UnsignedOrNone getPackSubstitutionIndex() const { - return UnsignedOrNone::fromInternalRepresentation( - Atomic.PackSubstitutionIndex); + 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 AtomicConstraint : public NormalizedConstraintWithParamMapping { - using NormalizedConstraintWithParamMapping:: - NormalizedConstraintWithParamMapping; -public: - static AtomicConstraint *Create(ASTContext &Ctx, const Expr *ConstraintExpr, - const NamedDecl *ConstraintDecl, - UnsignedOrNone PackIndex) { - return new (Ctx) - AtomicConstraint(ConstraintExpr, ConstraintDecl, PackIndex); + bool isAtomic() const { return llvm::isa(Constraint); } + bool isFoldExpanded() const { + return llvm::isa(Constraint); } + bool isCompound() const { return llvm::isa(Constraint); } - const Expr *getConstraintExpr() const { - return cast(Atomic.ConstraintExpr); - } -}; + CompoundConstraintKind getCompoundKind() const; -class FoldExpandedConstraint : public NormalizedConstraintWithParamMapping { - using NormalizedConstraintWithParamMapping:: - NormalizedConstraintWithParamMapping; + NormalizedConstraint &getLHS() const; + NormalizedConstraint &getRHS() 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); - } + AtomicConstraint *getAtomicConstraint() const; - using NormalizedConstraint::hasMatchingParameterMapping; + FoldExpandedConstraint *getFoldExpandedConstraint() const; - FoldOperatorKind getFoldOperator() const { - return static_cast(FoldExpanded.FoldOperator); - } +private: + static std::optional + fromAssociatedConstraints(Sema &S, const NamedDecl *D, + ArrayRef ACs); + static std::optional + fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E); +}; - const Expr *getPattern() const { return FoldExpanded.Pattern; } +struct alignas(ConstraintAlignment) NormalizedConstraintPair { + NormalizedConstraint LHS, RHS; +}; - const NormalizedConstraint &getNormalizedPattern() const { - return *FoldExpanded.Constraint; - } +struct alignas(ConstraintAlignment) FoldExpandedConstraint { + enum class FoldOperatorKind { And, Or } Kind; + NormalizedConstraint Constraint; + const Expr *Pattern; - NormalizedConstraint &getNormalizedPattern() { - return *FoldExpanded.Constraint; - } + FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C, + const Expr *Pattern) + : Kind(K), Constraint(std::move(C)), Pattern(Pattern) {}; static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A, const FoldExpandedConstraint &B); }; -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 UnsubstitutedConstraintSatisfactionCacheResult { - ExprResult SubstExpr; - ConstraintSatisfaction Satisfaction; -}; +const NormalizedConstraint *getNormalizedAssociatedConstraints( + Sema &S, const NamedDecl *ConstrainedDecl, + ArrayRef AssociatedConstraints); /// \brief SubsumptionChecker establishes subsumption /// between two set of constraints. @@ -465,13 +189,13 @@ class SubsumptionChecker { }; struct MappedAtomicConstraint { - const AtomicConstraint *Constraint; + AtomicConstraint *Constraint; Literal ID; }; struct FoldExpendedConstraintKey { FoldExpandedConstraint::FoldOperatorKind Kind; - const AtomicConstraint *Constraint; + AtomicConstraint *Constraint; Literal ID; }; @@ -483,7 +207,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. @@ -510,12 +234,12 @@ class SubsumptionChecker { FormulaType Normalize(const NormalizedConstraint &C); void AddUniqueClauseToFormula(Formula &F, Clause C); - Literal find(const AtomicConstraint *); - Literal find(const FoldExpandedConstraint *); + Literal find(AtomicConstraint *); + Literal find(FoldExpandedConstraint *); uint16_t getNewLiteralId(); }; -} // namespace clang +} // clang #endif // LLVM_CLANG_SEMA_SEMACONCEPT_H diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index 60c7d275f1aaf..115c19d4f1540 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -234,25 +234,21 @@ 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, - bool Final = false) { + void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) { 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?"); TemplateArgumentLists[0].Args = Args; - return; + } else { + --NumRetainedOuterLevels; + TemplateArgumentLists.push_back( + {{AssociatedDecl, /*Final=*/false}, Args}); } - --NumRetainedOuterLevels; - TemplateArgumentLists.push_back( - {{AssociatedDecl, /*Final=*/Final}, Args}); - } - - void replaceOutermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) { - assert((!TemplateArgumentLists.empty()) && "Replacing in an empty list?"); - TemplateArgumentLists.back().AssociatedDeclAndFinal.setPointer( - AssociatedDecl); - TemplateArgumentLists.back().Args = Args; } /// Add an outermost level that we are not substituting. We have no diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index fd12bc4e83827..d658890e076c2 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -24,18 +24,13 @@ static void CreateUnsatisfiedConstraintRecord(const ASTContext &C, const UnsatisfiedConstraintRecord &Detail, UnsatisfiedConstraintRecord *TrailingObject) { - if (Detail.isNull()) - new (TrailingObject) UnsatisfiedConstraintRecord(nullptr); - else if (const auto *E = llvm::dyn_cast(Detail)) + if (auto *E = 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); + *cast *>(Detail); StringRef Message = C.backupStr(SubstitutionDiagnostic.second); - auto *NewSubstDiag = new (C) clang::ConstraintSubstitutionDiagnostic( + auto *NewSubstDiag = new (C) std::pair( SubstitutionDiagnostic.first, Message); new (TrailingObject) UnsatisfiedConstraintRecord(NewSubstDiag); } @@ -79,10 +74,9 @@ 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) @@ -122,19 +116,6 @@ 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/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index f43fa8c90ad3b..1c8fd83feb7f8 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1069,22 +1069,22 @@ Error ASTNodeImporter::ImportConstraintSatisfaction( ToSat.ContainsErrors = FromSat.ContainsErrors; if (!ToSat.IsSatisfied) { for (auto Record = FromSat.begin(); Record != FromSat.end(); ++Record) { - if (const Expr *E = Record->dyn_cast()) { + if (Expr *E = Record->dyn_cast()) { ExpectedExpr ToSecondExpr = import(E); if (!ToSecondExpr) return ToSecondExpr.takeError(); ToSat.Details.emplace_back(ToSecondExpr.get()); } else { - auto Pair = - Record->dyn_cast(); + auto Pair = Record->dyn_cast *>(); ExpectedSLoc ToPairFirst = import(Pair->first); if (!ToPairFirst) return ToPairFirst.takeError(); StringRef ToPairSecond = ImportASTStringRef(Pair->second); - ToSat.Details.emplace_back(new (Importer.getToContext()) - ConstraintSubstitutionDiagnostic{ - ToPairFirst.get(), ToPairSecond}); + ToSat.Details.emplace_back( + new (Importer.getToContext()) + ConstraintSatisfaction::SubstitutionDiagnostic{ + ToPairFirst.get(), ToPairSecond}); } } } diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 40c9e49193ffe..dc6d232d9a525 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -12,11 +12,9 @@ #include "clang/Sema/SemaConcept.h" #include "TreeTransform.h" -#include "clang/AST/ASTConcept.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprConcepts.h" -#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/OperatorPrecedence.h" #include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/Initialization.h" @@ -29,7 +27,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/SaveAndRestore.h" +#include using namespace clang; using namespace sema; @@ -87,7 +85,7 @@ class LogicalBinOp { OK_Ordinary, Loc, FPOptionsOverride{}); } }; -} // namespace +} bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, Token NextToken, bool *PossibleNonPrimary, @@ -148,14 +146,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; } @@ -166,315 +164,52 @@ 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 = nullptr) { +static bool +DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID, + const NamedDecl *Templ, const Expr *E, + const MultiLevelTemplateArgumentList &MLTAL) { E->Profile(ID, S.Context, /*Canonical=*/true); - if (MLTAL) { - for (const auto &List : *MLTAL) - for (const auto &TemplateArg : List.Args) - S.Context.getCanonicalTemplateArgument(TemplateArg) - .Profile(ID, S.Context); - } + for (const auto &List : MLTAL) + for (const auto &TemplateArg : List.Args) + TemplateArg.Profile(ID, S.Context); + + // Note that we have to do this with our own collection, because there are + // times where a constraint-expression check can cause us to need to evaluate + // other constriants that are unrelated, such as when evaluating a recovery + // expression, or when trying to determine the constexpr-ness of special + // members. Otherwise we could just use the + // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function. if (S.SatisfactionStackContains(Templ, ID)) { S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self) << E << E->getSourceRange(); return true; } - return false; -} - -// Figure out the to-translation-unit depth for this function declaration for -// the purpose of seeing if they differ by constraints. This isn't the same as -// getTemplateDepth, because it includes already instantiated parents. -static unsigned -CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND, - bool SkipForSpecialization = false) { - MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - ND, ND->getLexicalDeclContext(), /*Final=*/false, - /*Innermost=*/std::nullopt, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true, SkipForSpecialization); - return MLTAL.getNumLevels(); -} - -namespace { -class AdjustConstraintDepth : public TreeTransform { - 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 { - -// FIXME: Convert it to DynamicRecursiveASTVisitor -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 TraverseTagType(const TagType *T, bool TraverseQualifier) { - // T's parent can be dependent while T doesn't have any template arguments. - // We should have already traversed its qualifier. - // FIXME: Add an assert to catch cases where we failed to profile the - // concept. assert(!T->isDependentType() && "We missed a case in profiling - // concepts!"); - return true; - } - - bool TraverseInjectedClassNameType(InjectedClassNameType *T, - bool TraverseQualifier) { - return TraverseTemplateArguments(T->getTemplateArgs(SemaRef.Context)); - } - - 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 (const 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); - } - - for (auto &Used : UsedTemplateArgs) { - llvm::FoldingSetNodeID R; - Used.Profile(R, SemaRef.Context); - ID.AddNodeID(R); - } - } -}; - -class ConstraintSatisfactionChecker { - 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 EvaluateSlow(const AtomicConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL); - - ExprResult Evaluate(const AtomicConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL); - - ExprResult EvaluateSlow(const FoldExpandedConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL); - - ExprResult Evaluate(const FoldExpandedConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL); - - ExprResult EvaluateSlow(const ConceptIdConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL, - unsigned int Size); - - ExprResult Evaluate(const ConceptIdConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL); - - ExprResult Evaluate(const CompoundConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL); - -public: - ConstraintSatisfactionChecker(Sema &SemaRef, const NamedDecl *Template, - SourceLocation TemplateNameLoc, - UnsignedOrNone PackSubstitutionIndex, - ConstraintSatisfaction &Satisfaction) - : S(SemaRef), Template(Template), TemplateNameLoc(TemplateNameLoc), - PackSubstitutionIndex(PackSubstitutionIndex), - Satisfaction(Satisfaction) {} - - ExprResult Evaluate(const NormalizedConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL); -}; - -StringRef allocateStringFromConceptDiagnostic(const Sema &S, - const PartialDiagnostic Diag) { - SmallString<128> DiagString; - DiagString = ": "; - Diag.EmitToString(S.getDiagnostics(), DiagString); - return S.getASTContext().backupStr(DiagString); + return false; } -} // namespace - -ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint( - const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL) { +static ExprResult EvaluateAtomicConstraint( + Sema &S, const Expr *AtomicExpr, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction) { 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 = const_cast(AtomicExpr); + ExprResult SubstitutedExpression; { TemplateDeductionInfo Info(TemplateNameLoc); Sema::InstantiatingTemplate Inst( @@ -485,6 +220,16 @@ ExprResult ConstraintSatisfactionChecker::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 = @@ -502,16 +247,21 @@ ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint( PartialDiagnosticAt SubstDiag{SourceLocation(), PartialDiagnostic::NullDiagnostic()}; Info.takeSFINAEDiagnostic(SubstDiag); - // FIXME: This is an unfortunate consequence of there + // FIXME: Concepts: This is an unfortunate consequence of there // being no serialization code for PartialDiagnostics and the fact // that serializing them would likely take a lot more storage than // just storing them as strings. We would still like, in the // future, to serialize the proper PartialDiagnostic as serializing // it as a string defeats the purpose of the diagnostic mechanism. + SmallString<128> DiagString; + DiagString = ": "; + SubstDiag.second.EmitToString(S.getDiagnostics(), DiagString); + unsigned MessageSize = DiagString.size(); + char *Mem = new (S.Context) char[MessageSize]; + memcpy(Mem, DiagString.c_str(), MessageSize); Satisfaction.Details.emplace_back( - new (S.Context) ConstraintSubstitutionDiagnostic{ - SubstDiag.first, - allocateStringFromConceptDiagnostic(S, SubstDiag.second)}); + new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ + SubstDiag.first, StringRef(Mem, MessageSize)}); Satisfaction.IsSatisfied = false; return ExprEmpty(); } @@ -539,525 +289,284 @@ ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint( return SubstitutedExpression; } -std::optional -ConstraintSatisfactionChecker::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; - const 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 ConstraintSatisfactionChecker::EvaluateSlow( - 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); - Satisfaction.Details.emplace_back( - new (S.Context) ConstraintSubstitutionDiagnostic{ - SubstitutedAtomicExpr.get()->getBeginLoc(), - allocateStringFromConceptDiagnostic(S, Msg)}); - 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 ConstraintSatisfactionChecker::Evaluate( - const AtomicConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL) { - - unsigned Size = Satisfaction.Details.size(); - llvm::FoldingSetNodeID ID; - UnsignedOrNone OuterPackSubstIndex = - Constraint.getPackSubstitutionIndex() - ? Constraint.getPackSubstitutionIndex() - : PackSubstitutionIndex; - - ID.AddPointer(Constraint.getConstraintExpr()); - ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation()); - HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex) - .VisitConstraint(Constraint); - - if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID); - Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) { - - 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()); - return Iter->second.SubstExpr; - } - - ExprResult E = EvaluateSlow(Constraint, MLTAL); - - UnsubstitutedConstraintSatisfactionCacheResult 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; - S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)}); - - return E; -} - -UnsignedOrNone -ConstraintSatisfactionChecker::EvaluateFoldExpandedConstraintSize( - const FoldExpandedConstraint &FE, - const MultiLevelTemplateArgumentList &MLTAL) { +static UnsignedOrNone EvaluateFoldExpandedConstraintSize( + Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction) { // We should ignore errors in the presence of packs of different size. Sema::SFINAETrap Trap(S); - Expr *Pattern = const_cast(FE.getPattern()); + Expr *Pattern = FE->getPattern(); SmallVector Unexpanded; S.collectUnexpandedParameterPacks(Pattern, Unexpanded); assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); bool Expand = true; bool RetainExpansion = false; - UnsignedOrNone NumExpansions(std::nullopt); + UnsignedOrNone NumExpansions = FE->getNumExpansions(); if (S.CheckParameterPacksForExpansion( - Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL, - /*FailOnPackProducingTemplates=*/false, Expand, RetainExpansion, + FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL, + /*FailOnPackProducingTemplates=*/true, Expand, RetainExpansion, NumExpansions) || !Expand || RetainExpansion) return std::nullopt; if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) { - S.Diag(Pattern->getExprLoc(), + S.Diag(FE->getEllipsisLoc(), clang::diag::err_fold_expression_limit_exceeded) << *NumExpansions << S.getLangOpts().BracketDepth - << Pattern->getSourceRange(); - S.Diag(Pattern->getExprLoc(), diag::note_bracket_depth); + << FE->getSourceRange(); + S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth); return std::nullopt; } return NumExpansions; } -ExprResult ConstraintSatisfactionChecker::EvaluateSlow( - 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(); - } +static ExprResult calculateConstraintSatisfaction( + Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction); - ExprResult Out; - UnsignedOrNone NumExpansions = - EvaluateFoldExpandedConstraintSize(Constraint, *SubstitutedArgs); - if (!NumExpansions) - return ExprEmpty(); +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(); - if (*NumExpansions == 0) { - Satisfaction.IsSatisfied = Conjunction; - return ExprEmpty(); - } + ExprResult LHSRes = calculateConstraintSatisfaction( + S, LHS, Template, TemplateNameLoc, MLTAL, Satisfaction); - for (unsigned I = 0; I < *NumExpansions; I++) { - Sema::ArgPackSubstIndexRAII SubstIndex(S, I); - Satisfaction.IsSatisfied = false; - Satisfaction.ContainsErrors = false; - ExprResult Expr = - ConstraintSatisfactionChecker(S, Template, TemplateNameLoc, - UnsignedOrNone(I), Satisfaction) - .Evaluate(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 (!Conjunction && Satisfaction.IsSatisfied) { - Satisfaction.Details.erase(Satisfaction.Details.begin() + - EffectiveDetailEndIndex, - Satisfaction.Details.end()); - break; - } - if (Satisfaction.IsSatisfied != Conjunction) - return Out; - } + if (LHSRes.isInvalid()) + return ExprError(); - return Out; -} + bool IsLHSSatisfied = Satisfaction.IsSatisfied; + + if (Op == clang::OO_PipePipe && IsLHSSatisfied) + // [temp.constr.op] p3 + // A disjunction is a constraint taking two operands. To determine if + // a disjunction is satisfied, the satisfaction of the first operand + // is checked. If that is satisfied, the disjunction is satisfied. + // Otherwise, the disjunction is satisfied if and only if the second + // operand is satisfied. + // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp. + return LHSRes; + + if (Op == clang::OO_AmpAmp && !IsLHSSatisfied) + // [temp.constr.op] p2 + // A conjunction is a constraint taking two operands. To determine if + // a conjunction is satisfied, the satisfaction of the first operand + // is checked. If that is not satisfied, the conjunction is not + // satisfied. Otherwise, the conjunction is satisfied if and only if + // the second operand is satisfied. + // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp. + return LHSRes; + + ExprResult RHSRes = calculateConstraintSatisfaction( + S, RHS, Template, TemplateNameLoc, MLTAL, Satisfaction); + if (RHSRes.isInvalid()) + return ExprError(); -ExprResult ConstraintSatisfactionChecker::Evaluate( - const FoldExpandedConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL) { + 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(); - llvm::FoldingSetNodeID ID; - ID.AddPointer(Constraint.getPattern()); - HashParameterMapping(S, MLTAL, ID, std::nullopt).VisitConstraint(Constraint); - - if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID); - Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) { - - auto &Cached = Iter->second.Satisfaction; - Satisfaction.ContainsErrors = Cached.ContainsErrors; - Satisfaction.IsSatisfied = Cached.IsSatisfied; - Satisfaction.Details.insert(Satisfaction.Details.end(), - Cached.Details.begin(), Cached.Details.end()); - return Iter->second.SubstExpr; - } - - unsigned Size = Satisfaction.Details.size(); - - ExprResult E = EvaluateSlow(Constraint, MLTAL); - UnsubstitutedConstraintSatisfactionCacheResult 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; - S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)}); - return E; + return BinaryOperator::Create(S.Context, LHSRes.get(), RHSRes.get(), + BinaryOperator::getOverloadedOpcode(Op), + S.Context.BoolTy, VK_PRValue, OK_Ordinary, + LHS->getBeginLoc(), FPOptionsOverride{}); } -ExprResult ConstraintSatisfactionChecker::EvaluateSlow( - const ConceptIdConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL, unsigned Size) { - const ConceptReference *ConceptId = Constraint.getConceptId(); +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(); - llvm::SmallVector SubstitutedOuterMost; - std::optional SubstitutedArgs = - SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); + ExprResult Out; + if (FE->isLeftFold() && FE->getInit()) { + Out = calculateConstraintSatisfaction(S, FE->getInit(), Template, + TemplateNameLoc, MLTAL, Satisfaction); + if (Out.isInvalid()) + return ExprError(); - if (!SubstitutedArgs) { - Satisfaction.IsSatisfied = false; - // FIXME: diagnostics? + // If the first clause of a conjunction is not satisfied, + // or if the first clause of a disjection is satisfied, + // we have established satisfaction of the whole constraint + // and we should not continue further. + if (Conjunction != Satisfaction.IsSatisfied) + return Out; + } + UnsignedOrNone NumExpansions = EvaluateFoldExpandedConstraintSize( + S, FE, Template, TemplateNameLoc, MLTAL, Satisfaction); + if (!NumExpansions) return ExprError(); + for (unsigned I = 0; I < *NumExpansions; I++) { + Sema::ArgPackSubstIndexRAII SubstIndex(S, I); + ExprResult Res = calculateConstraintSatisfaction( + S, FE->getPattern(), Template, TemplateNameLoc, MLTAL, Satisfaction); + if (Res.isInvalid()) + return ExprError(); + bool IsRHSSatisfied = Satisfaction.IsSatisfied; + if (!Conjunction && IsRHSSatisfied) { + auto EffectiveDetailEnd = Satisfaction.Details.begin(); + std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex); + Satisfaction.Details.erase(EffectiveDetailEnd, + Satisfaction.Details.end()); + } + if (Out.isUnset()) + Out = Res; + else if (!Res.isUnset()) { + Out = BinaryOperator::Create( + S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy, + VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{}); + } + if (Conjunction != IsRHSSatisfied) + return Out; } - Sema::SFINAETrap Trap(S); - Sema::ArgPackSubstIndexRAII SubstIndex( - S, Constraint.getPackSubstitutionIndex() - ? Constraint.getPackSubstitutionIndex() - : PackSubstitutionIndex); - - const ASTTemplateArgumentListInfo *Ori = - ConceptId->getTemplateArgsAsWritten(); - TemplateDeductionInfo Info(TemplateNameLoc); - Sema::InstantiatingTemplate _( - 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; - if (!Trap.hasErrorOccurred()) + if (FE->isRightFold() && FE->getInit()) { + ExprResult Res = calculateConstraintSatisfaction( + S, FE->getInit(), Template, TemplateNameLoc, MLTAL, Satisfaction); + if (Out.isInvalid()) return ExprError(); - PartialDiagnosticAt SubstDiag{SourceLocation(), - PartialDiagnostic::NullDiagnostic()}; - Info.takeSFINAEDiagnostic(SubstDiag); - // FIXME: 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. - Satisfaction.Details.insert( - Satisfaction.Details.begin() + Size, - new (S.Context) ConstraintSubstitutionDiagnostic{ - SubstDiag.first, - allocateStringFromConceptDiagnostic(S, SubstDiag.second)}); - 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{}); + } } - CXXScopeSpec SS; - SS.Adopt(ConceptId->getNestedNameSpecifierLoc()); - - ExprResult SubstitutedConceptId = S.CheckConceptTemplateId( - SS, ConceptId->getTemplateKWLoc(), ConceptId->getConceptNameInfo(), - ConceptId->getFoundDecl(), ConceptId->getNamedConcept(), &OutArgs, - /*DoCheckConstraintSatisfaction=*/false); - - if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred()) - return ExprError(); - - if (Size != Satisfaction.Details.size()) { - Satisfaction.Details.insert( - Satisfaction.Details.begin() + Size, - UnsatisfiedConstraintRecord( - SubstitutedConceptId.getAs() - ->getConceptReference())); + if (Out.isUnset()) { + Satisfaction.IsSatisfied = Conjunction; + Out = S.BuildEmptyCXXFoldExpr(FE->getBeginLoc(), FE->getOperator()); } - return SubstitutedConceptId; + return Out; } -ExprResult ConstraintSatisfactionChecker::Evaluate( - const ConceptIdConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL) { - - const ConceptReference *ConceptId = Constraint.getConceptId(); - - UnsignedOrNone OuterPackSubstIndex = - Constraint.getPackSubstitutionIndex() - ? Constraint.getPackSubstitutionIndex() - : PackSubstitutionIndex; - - Sema::InstantiatingTemplate _(S, ConceptId->getBeginLoc(), - Sema::InstantiatingTemplate::ConstraintsCheck{}, - ConceptId->getNamedConcept(), - MLTAL.getInnermost(), - Constraint.getSourceRange()); - - unsigned Size = Satisfaction.Details.size(); +static ExprResult calculateConstraintSatisfaction( + Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template, + SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, + ConstraintSatisfaction &Satisfaction) { + ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts(); - ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL); + if (LogicalBinOp BO = ConstraintExpr) + return calculateConstraintSatisfaction( + S, BO.getLHS(), BO.getOp(), BO.getRHS(), Template, TemplateNameLoc, + MLTAL, Satisfaction); - if (!E.isUsable()) { - Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, ConceptId); - return E; + 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); } - // ConceptIdConstraint is only relevant for diagnostics, - // so if the normalized constraint is satisfied, we should not - // substitute into the constraint. - if (Satisfaction.IsSatisfied) - return E; - - llvm::FoldingSetNodeID ID; - ID.AddPointer(Constraint.getConceptId()); - ID.AddInteger(OuterPackSubstIndex.toInternalRepresentation()); - HashParameterMapping(S, MLTAL, ID, OuterPackSubstIndex) - .VisitConstraint(Constraint); - - if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID); - Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) { - - 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()); - return Iter->second.SubstExpr; - } - - ExprResult CE = EvaluateSlow(Constraint, MLTAL, Size); - if (CE.isInvalid()) - return E; - UnsubstitutedConstraintSatisfactionCacheResult 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 = CE; - S.UnsubstitutedConstraintSatisfactionCache.insert({ID, std::move(Cache)}); - return CE; -} - -ExprResult ConstraintSatisfactionChecker::Evaluate( - const CompoundConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL) { - - unsigned EffectiveDetailEndIndex = Satisfaction.Details.size(); - - bool Conjunction = - Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction; + 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 LHS = Evaluate(Constraint.getLHS(), MLTAL); + // FIXME: We should not treat ConceptSpecializationExpr as atomic constraints. - if (Conjunction && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors)) - return LHS; + // An atomic constraint expression + ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint( + S, ConstraintExpr, Template, TemplateNameLoc, MLTAL, Satisfaction); - if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied && - !Satisfaction.ContainsErrors) - return LHS; + if (SubstitutedAtomicExpr.isInvalid()) + return ExprError(); - Satisfaction.ContainsErrors = false; - Satisfaction.IsSatisfied = false; + if (!SubstitutedAtomicExpr.isUsable()) + // Evaluator has decided satisfaction without yielding an expression. + return ExprEmpty(); - ExprResult RHS = Evaluate(Constraint.getRHS(), MLTAL); + // 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; - if (RHS.isUsable() && Satisfaction.IsSatisfied && - !Satisfaction.ContainsErrors) - Satisfaction.Details.erase(Satisfaction.Details.begin() + - EffectiveDetailEndIndex, - Satisfaction.Details.end()); + 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 (!LHS.isUsable()) - return RHS; + 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(); + } - if (!RHS.isUsable()) - return LHS; + 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 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{}); + return SubstitutedAtomicExpr; } -ExprResult ConstraintSatisfactionChecker::Evaluate( - const NormalizedConstraint &Constraint, - const MultiLevelTemplateArgumentList &MLTAL) { - switch (Constraint.getKind()) { - case NormalizedConstraint::ConstraintKind::Atomic: - return Evaluate(static_cast(Constraint), MLTAL); - - case NormalizedConstraint::ConstraintKind::FoldExpanded: - return Evaluate(static_cast(Constraint), - MLTAL); - - case NormalizedConstraint::ConstraintKind::ConceptId: - return Evaluate(static_cast(Constraint), - MLTAL); +static ExprResult calculateConstraintSatisfaction( + Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc, + const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction) { - case NormalizedConstraint::ConstraintKind::Compound: - return Evaluate(static_cast(Constraint), MLTAL); - } + return calculateConstraintSatisfaction(S, ConstraintExpr, Template, + TemplateNameLoc, MLTAL, Satisfaction); } static bool CheckConstraintSatisfaction( Sema &S, const NamedDecl *Template, ArrayRef AssociatedConstraints, + llvm::SmallVectorImpl &Converted, const MultiLevelTemplateArgumentList &TemplateArgsLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction, - Expr **ConvertedExpr, const ConceptReference *TopLevelConceptId = nullptr) { - - if (ConvertedExpr) - *ConvertedExpr = nullptr; - + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { if (AssociatedConstraints.empty()) { Satisfaction.IsSatisfied = true; return false; @@ -1069,60 +578,57 @@ static bool CheckConstraintSatisfaction( return false; } - 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; + 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()) return true; - } - - if (TopLevelConceptId) - C = ConceptIdConstraint::Create(S.getASTContext(), TopLevelConceptId, - const_cast(C), - Template, /*CSE=*/nullptr, - S.ArgPackSubstIndex); - ExprResult Res = - ConstraintSatisfactionChecker(S, Template, TemplateIDRange.getBegin(), - S.ArgPackSubstIndex, Satisfaction) - .Evaluate(*C, TemplateArgsLists); - - if (Res.isInvalid()) - return true; + for (const AssociatedConstraint &AC : AssociatedConstraints) { + if (AC.isNull()) + return true; - if (Res.isUsable() && ConvertedExpr) - *ConvertedExpr = Res.get(); + Sema::ArgPackSubstIndexRAII _(S, AC.ArgPackSubstIndex); + ExprResult Res = calculateConstraintSatisfaction( + S, Template, TemplateIDRange.getBegin(), TemplateArgsLists, + AC.ConstraintExpr, Satisfaction); + if (Res.isInvalid()) + return true; + 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( - ConstrainedDeclOrNestedRequirement Entity, + const NamedDecl *Template, ArrayRef AssociatedConstraints, + llvm::SmallVectorImpl &ConvertedConstraints, const MultiLevelTemplateArgumentList &TemplateArgsLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction, - const ConceptReference *TopLevelConceptId, Expr **ConvertedExpr) { + SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { if (AssociatedConstraints.empty()) { OutSatisfaction.IsSatisfied = true; return false; } - const auto *Template = Entity.dyn_cast(); if (!Template) { return ::CheckConstraintSatisfaction( - *this, nullptr, AssociatedConstraints, TemplateArgsLists, - TemplateIDRange, OutSatisfaction, ConvertedExpr, TopLevelConceptId); + *this, nullptr, AssociatedConstraints, ConvertedConstraints, + TemplateArgsLists, TemplateIDRange, OutSatisfaction); } // Invalid templates could make their way here. Substituting them could result // in dependent expressions. @@ -1137,15 +643,10 @@ bool Sema::CheckConstraintSatisfaction( // here. llvm::SmallVector FlattenedArgs; for (auto List : TemplateArgsLists) - for (const TemplateArgument &Arg : List.Args) - FlattenedArgs.emplace_back(Context.getCanonicalTemplateArgument(Arg)); - - const NamedDecl *Owner = Template; - if (TopLevelConceptId) - Owner = TopLevelConceptId->getNamedConcept(); + llvm::append_range(FlattenedArgs, List.Args); llvm::FoldingSetNodeID ID; - ConstraintSatisfaction::Profile(ID, Context, Owner, FlattenedArgs); + ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs); void *InsertPos; if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) { OutSatisfaction = *Cached; @@ -1153,10 +654,10 @@ bool Sema::CheckConstraintSatisfaction( } auto Satisfaction = - std::make_unique(Owner, FlattenedArgs); - if (::CheckConstraintSatisfaction( - *this, Template, AssociatedConstraints, TemplateArgsLists, - TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) { + std::make_unique(Template, FlattenedArgs); + if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints, + ConvertedConstraints, TemplateArgsLists, + TemplateIDRange, *Satisfaction)) { OutSatisfaction = *Satisfaction; return true; } @@ -1187,18 +688,14 @@ 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 CheckConstraintSatisfaction( - ConstraintExpr->getNamedConcept(), Constraints, MLTAL, - ConstraintExpr->getSourceRange(), Satisfaction, - ConstraintExpr->getConceptReference()); + return calculateConstraintSatisfaction( + *this, ConstraintExpr, ConstraintExpr->getNamedConcept(), + ConstraintExpr->getConceptNameLoc(), MLTAL, Satisfaction) + .isInvalid(); } bool Sema::SetupConstraintScope( @@ -1357,6 +854,50 @@ 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) { @@ -1620,61 +1161,73 @@ 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(); - S.DiagnoseUnsatisfiedConstraint(ConstraintExpr); - 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(); + 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"); } } 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"); @@ -1682,9 +1235,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) @@ -1696,53 +1249,31 @@ static void diagnoseUnsatisfiedRequirement(Sema &S, return; } } - -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 diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, + Expr *SubstExpr, + bool First = true); static void diagnoseUnsatisfiedRequirement(Sema &S, concepts::NestedRequirement *Req, bool First) { - DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(), - Req->hasInvalidConstraint() - ? SourceLocation() - : Req->getConstraintExpr()->getExprLoc(), - First, Req); + 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; + } } static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, - const Expr *SubstExpr, + Expr *SubstExpr, bool First) { SubstExpr = SubstExpr->IgnoreParenImpCasts(); - if (const BinaryOperator *BO = dyn_cast(SubstExpr)) { + if (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 @@ -1788,7 +1319,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 @@ -1803,6 +1334,22 @@ 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()) @@ -1817,10 +1364,6 @@ 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); @@ -1836,332 +1379,216 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, S.DiagnoseTypeTraitDetails(SubstExpr); } +template static void diagnoseUnsatisfiedConstraintExpr( - Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc, - bool First, concepts::NestedRequirement *Req) { - if (auto *Diag = - Record - .template dyn_cast()) { - 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; - } - if (const auto *Concept = dyn_cast(Record)) { - if (Loc.isInvalid()) - Loc = Concept->getBeginLoc(); - diagnoseUnsatisfiedConceptIdExpr(S, Concept, Loc, First); + 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; return; } - diagnoseWellFormedUnsatisfiedConstraintExpr( - S, cast(Record), First); -} -void Sema::DiagnoseUnsatisfiedConstraint( - const ConstraintSatisfaction &Satisfaction, SourceLocation Loc, - bool First) { + diagnoseWellFormedUnsatisfiedConstraintExpr(S, cast(Record), First); +} +void +Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.Details, Loc, First); + for (auto &Record : Satisfaction.Details) { + diagnoseUnsatisfiedConstraintExpr(*this, Record, First); + First = false; + } } void Sema::DiagnoseUnsatisfiedConstraint( - const ConceptSpecializationExpr *ConstraintExpr, bool First) { - - const ASTConstraintSatisfaction &Satisfaction = - ConstraintExpr->getSatisfaction(); - + const ASTConstraintSatisfaction &Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - - ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(), - ConstraintExpr->getBeginLoc(), First); + for (auto &Record : Satisfaction) { + diagnoseUnsatisfiedConstraintExpr(*this, Record, First); + First = false; + } } -namespace { - -class SubstituteParameterMappings { - Sema &SemaRef; - - const MultiLevelTemplateArgumentList *MLTAL; - const ASTTemplateArgumentListInfo *ArgsAsWritten; - - bool InFoldExpr; - - SubstituteParameterMappings(Sema &SemaRef, - const MultiLevelTemplateArgumentList *MLTAL, - const ASTTemplateArgumentListInfo *ArgsAsWritten, - bool InFoldExpr) - : SemaRef(SemaRef), MLTAL(MLTAL), ArgsAsWritten(ArgsAsWritten), - InFoldExpr(InFoldExpr) {} - - void buildParameterMapping(NormalizedConstraintWithParamMapping &N); - - bool substitute(NormalizedConstraintWithParamMapping &N); +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()); - bool substitute(ConceptIdConstraint &CC); + 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; +} -public: - SubstituteParameterMappings(Sema &SemaRef, bool InFoldExpr = false) - : SemaRef(SemaRef), MLTAL(nullptr), ArgsAsWritten(nullptr), - InFoldExpr(InFoldExpr) {} +const NormalizedConstraint *clang::getNormalizedAssociatedConstraints( + Sema &S, const NamedDecl *ConstrainedDecl, + ArrayRef AssociatedConstraints) { + return S.getNormalizedAssociatedConstraints(ConstrainedDecl, + AssociatedConstraints); +} - bool substitute(NormalizedConstraint &N); -}; +static bool +substituteParameterMappings(Sema &S, NormalizedConstraint &N, + ConceptDecl *Concept, + const MultiLevelTemplateArgumentList &MLTAL, + const ASTTemplateArgumentListInfo *ArgsAsWritten) { -void SubstituteParameterMappings::buildParameterMapping( - NormalizedConstraintWithParamMapping &N) { - TemplateParameterList *TemplateParams = - cast(N.getConstraintDecl())->getTemplateParameters(); - - llvm::SmallBitVector OccurringIndices(TemplateParams->size()); - llvm::SmallBitVector OccurringIndicesForSubsumption(TemplateParams->size()); - - if (N.getKind() == NormalizedConstraint::ConstraintKind::Atomic) { - SemaRef.MarkUsedTemplateParameters( - static_cast(N).getConstraintExpr(), - /*OnlyDeduced=*/false, - /*Depth=*/0, OccurringIndices); - - SemaRef.MarkUsedTemplateParametersForSubsumptionParameterMapping( - static_cast(N).getConstraintExpr(), - /*Depth=*/0, OccurringIndicesForSubsumption); - - } 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); + if (N.isCompound()) { + if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL, + ArgsAsWritten)) + return true; + return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL, + ArgsAsWritten); } - 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++; - } + + if (N.isFoldExpanded()) { + Sema::ArgPackSubstIndexRAII _(S, std::nullopt); + return substituteParameterMappings( + S, N.getFoldExpandedConstraint()->Constraint, Concept, MLTAL, + ArgsAsWritten); } - auto *UsedList = TemplateParameterList::Create( - SemaRef.Context, TemplateParams->getTemplateLoc(), - TemplateParams->getLAngleLoc(), UsedParams, - /*RAngleLoc=*/SourceLocation(), - /*RequiresClause=*/nullptr); - unsigned Size = OccurringIndices.count(); - N.updateParameterMapping( - std::move(OccurringIndices), std::move(OccurringIndicesForSubsumption), - MutableArrayRef{TempArgs, Size}, UsedList); -} -bool SubstituteParameterMappings::substitute( - NormalizedConstraintWithParamMapping &N) { - if (!N.hasParameterMapping()) - buildParameterMapping(N); + TemplateParameterList *TemplateParams = Concept->getTemplateParameters(); - 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(); - } + 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(); Sema::InstantiatingTemplate Inst( - SemaRef, InstLocBegin, + S, InstLocBegin, Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, - const_cast(N.getConstraintDecl()), + const_cast(Atomic.ConstraintDecl), {InstLocBegin, InstLocEnd}); if (Inst.isInvalid()) return true; - - // 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)) + if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs)) return true; TemplateArgumentLoc *TempArgs = - 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(), - N.mappingOccurenceListForSubsumption(), Mapping, - N.getUsedTemplateParamList()); + new (S.Context) TemplateArgumentLoc[SubstArgs.size()]; + std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(), + TempArgs); + Atomic.ParameterMapping.emplace(TempArgs, SubstArgs.size()); return false; } -bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) { - assert(CC.getConstraintDecl() && MLTAL && ArgsAsWritten); - - if (substitute(static_cast(CC))) - return true; +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); - auto *CSE = CC.getConceptSpecializationExpr(); - assert(CSE); - assert(!CC.getBeginLoc().isInvalid()); + return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL, + CSE->getTemplateArgsAsWritten()); +} - SourceLocation InstLocBegin, InstLocEnd; - if (llvm::ArrayRef Arguments = ArgsAsWritten->arguments(); - Arguments.empty()) { - InstLocBegin = ArgsAsWritten->getLAngleLoc(); - InstLocEnd = ArgsAsWritten->getRAngleLoc(); +NormalizedConstraint::NormalizedConstraint(ASTContext &C, + NormalizedConstraint LHS, + NormalizedConstraint RHS, + CompoundConstraintKind Kind) + : Constraint{CompoundConstraint{ + new(C) NormalizedConstraintPair{std::move(LHS), std::move(RHS)}, + Kind}} {} + +NormalizedConstraint::NormalizedConstraint(ASTContext &C, + const NormalizedConstraint &Other) { + if (Other.isAtomic()) { + Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint()); + } else if (Other.isFoldExpanded()) { + Constraint = new (C) FoldExpandedConstraint( + Other.getFoldExpandedConstraint()->Kind, + NormalizedConstraint(C, Other.getFoldExpandedConstraint()->Constraint), + Other.getFoldExpandedConstraint()->Pattern); } else { - auto SR = Arguments[0].getSourceRange(); - InstLocBegin = SR.getBegin(); - InstLocEnd = SR.getEnd(); + Constraint = CompoundConstraint( + new (C) + NormalizedConstraintPair{NormalizedConstraint(C, Other.getLHS()), + NormalizedConstraint(C, Other.getRHS())}, + Other.getCompoundKind()); } - // 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; - - 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()); } -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); - - 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()); - } - } +NormalizedConstraint &NormalizedConstraint::getLHS() const { + assert(isCompound() && "getLHS called on a non-compound constraint."); + return cast(Constraint).getPointer()->LHS; } -} // namespace +NormalizedConstraint &NormalizedConstraint::getRHS() const { + assert(isCompound() && "getRHS called on a non-compound constraint."); + return cast(Constraint).getPointer()->RHS; +} -NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints( +std::optional +NormalizedConstraint::fromAssociatedConstraints( Sema &S, const NamedDecl *D, ArrayRef ACs) { assert(ACs.size() != 0); - auto *Conjunction = - fromConstraintExpr(S, D, ACs[0].ConstraintExpr, ACs[0].ArgPackSubstIndex); + auto Conjunction = fromConstraintExpr(S, D, ACs[0].ConstraintExpr); if (!Conjunction) - return nullptr; + return std::nullopt; for (unsigned I = 1; I < ACs.size(); ++I) { - auto *Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr, - ACs[I].ArgPackSubstIndex); + auto Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr); if (!Next) - return nullptr; - Conjunction = CompoundConstraint::CreateConjunction(S.getASTContext(), - Conjunction, Next); + return std::nullopt; + *Conjunction = NormalizedConstraint(S.Context, std::move(*Conjunction), + std::move(*Next), CCK_Conjunction); } return Conjunction; } -NormalizedConstraint *NormalizedConstraint::fromConstraintExpr( - Sema &S, const NamedDecl *D, const Expr *E, UnsignedOrNone SubstIndex) { +std::optional +NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, + const Expr *E) { assert(E != nullptr); // C++ [temp.constr.normal]p1.1 @@ -2170,29 +1597,23 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr( // [...] 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(), SubstIndex); + auto LHS = fromConstraintExpr(S, D, BO.getLHS()); if (!LHS) - return nullptr; - auto *RHS = fromConstraintExpr(S, D, BO.getRHS(), SubstIndex); + return std::nullopt; + auto RHS = fromConstraintExpr(S, D, BO.getRHS()); if (!RHS) - return nullptr; + return std::nullopt; - return CompoundConstraint::Create( - S.Context, LHS, BO.isAnd() ? CCK_Conjunction : CCK_Disjunction, RHS); + return NormalizedConstraint(S.Context, std::move(*LHS), std::move(*RHS), + BO.isAnd() ? CCK_Conjunction : CCK_Disjunction); } else if (auto *CSE = dyn_cast(E)) { - NormalizedConstraint *SubNF; + const NormalizedConstraint *SubNF; { Sema::InstantiatingTemplate Inst( S, CSE->getExprLoc(), @@ -2200,7 +1621,7 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr( // FIXME: improve const-correctness of InstantiatingTemplate const_cast(D), CSE->getSourceRange()); if (Inst.isInvalid()) - return nullptr; + return std::nullopt; // C++ [temp.constr.normal]p1.1 // [...] // The normal form of an id-expression of the form C, @@ -2210,21 +1631,20 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr( // constraint. If any such substitution results in an invalid type or // expression, the program is ill-formed; no diagnostic is required. // [...] - - // Use canonical declarations to merge ConceptDecls across - // different modules. - ConceptDecl *CD = CSE->getNamedConcept()->getCanonicalDecl(); - SubNF = NormalizedConstraint::fromAssociatedConstraints( - S, CD, AssociatedConstraint(CD->getConstraintExpr(), SubstIndex)); - + ConceptDecl *CD = CSE->getNamedConcept(); + SubNF = S.getNormalizedAssociatedConstraints( + CD, AssociatedConstraint(CD->getConstraintExpr())); if (!SubNF) - return nullptr; + return std::nullopt; } - return ConceptIdConstraint::Create(S.getASTContext(), - CSE->getConceptReference(), SubNF, D, - CSE, SubstIndex); + std::optional New; + New.emplace(S.Context, *SubNF); + + if (substituteParameterMappings(S, *New, CSE)) + return std::nullopt; + return New; } else if (auto *FE = dyn_cast(E); FE && S.getLangOpts().CPlusPlus26 && (FE->getOperator() == BinaryOperatorKind::BO_LAnd || @@ -2238,61 +1658,31 @@ NormalizedConstraint *NormalizedConstraint::fromConstraintExpr( : FoldExpandedConstraint::FoldOperatorKind::Or; if (FE->getInit()) { - auto *LHS = fromConstraintExpr(S, D, FE->getLHS(), SubstIndex); - auto *RHS = fromConstraintExpr(S, D, FE->getRHS(), SubstIndex); + auto LHS = fromConstraintExpr(S, D, FE->getLHS()); + auto RHS = fromConstraintExpr(S, D, FE->getRHS()); if (!LHS || !RHS) - return nullptr; + return std::nullopt; if (FE->isRightFold()) - LHS = FoldExpandedConstraint::Create(S.getASTContext(), - FE->getPattern(), D, Kind, LHS); + RHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{ + Kind, std::move(*RHS), FE->getPattern()}}; else - 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); + 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); } - auto *Sub = fromConstraintExpr(S, D, FE->getPattern(), SubstIndex); + auto Sub = fromConstraintExpr(S, D, FE->getPattern()); if (!Sub) - 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 std::nullopt; + return NormalizedConstraint{new (S.Context) FoldExpandedConstraint{ + Kind, std::move(*Sub), FE->getPattern()}}; } - // 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; + return NormalizedConstraint{new (S.Context) AtomicConstraint(E, D)}; } bool FoldExpandedConstraint::AreCompatibleForSubsumption( @@ -2303,10 +1693,8 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption( // if their respective constraints both contain an equivalent unexpanded pack. llvm::SmallVector APacks, BPacks; - Sema::collectUnexpandedParameterPacks(const_cast(A.getPattern()), - APacks); - Sema::collectUnexpandedParameterPacks(const_cast(B.getPattern()), - BPacks); + Sema::collectUnexpandedParameterPacks(const_cast(A.Pattern), APacks); + Sema::collectUnexpandedParameterPacks(const_cast(B.Pattern), BPacks); for (const UnexpandedParameterPack &APack : APacks) { auto ADI = getDepthAndIndex(APack); @@ -2400,7 +1788,7 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic( const AtomicConstraint &B) { if (!A.hasMatchingParameterMapping(Context, B)) return false; - const Expr *EA = A.getConstraintExpr(), *EB = B.getConstraintExpr(); + const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr; if (EA == EB) return true; @@ -2453,6 +1841,24 @@ 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 ----------------------------------- @@ -2468,8 +1874,8 @@ uint16_t SubsumptionChecker::getNewLiteralId() { return NextID++; } -auto SubsumptionChecker::find(const AtomicConstraint *Ori) -> Literal { - auto &Elems = AtomicMap[Ori->getConstraintExpr()]; +auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal { + auto &Elems = AtomicMap[Ori->ConstraintExpr]; // C++ [temp.constr.order] p2 // - an atomic constraint A subsumes another atomic constraint B // if and only if the A and B are identical [...] @@ -2485,16 +1891,13 @@ auto SubsumptionChecker::find(const AtomicConstraint *Ori) -> Literal { // subsumes another, their literal will be the same llvm::FoldingSetNodeID ID; - ID.AddBoolean(Ori->hasParameterMapping()); - if (Ori->hasParameterMapping()) { - const auto &Mapping = Ori->getParameterMapping(); - const NormalizedConstraint::OccurenceList &Indexes = - Ori->mappingOccurenceListForSubsumption(); - for (auto [Idx, TAL] : llvm::enumerate(Mapping)) { - if (Indexes[Idx]) - SemaRef.getASTContext() - .getCanonicalTemplateArgument(TAL.getArgument()) - .Profile(ID, SemaRef.getASTContext()); + const auto &Mapping = Ori->ParameterMapping; + ID.AddBoolean(Mapping.has_value()); + if (Mapping) { + for (const TemplateArgumentLoc &TAL : *Mapping) { + SemaRef.getASTContext() + .getCanonicalTemplateArgument(TAL.getArgument()) + .Profile(ID, SemaRef.getASTContext()); } } auto It = Elems.find(ID); @@ -2509,11 +1912,11 @@ auto SubsumptionChecker::find(const AtomicConstraint *Ori) -> Literal { return It->getSecond().ID; } -auto SubsumptionChecker::find(const FoldExpandedConstraint *Ori) -> Literal { - auto &Elems = FoldMap[Ori->getPattern()]; +auto SubsumptionChecker::find(FoldExpandedConstraint *Ori) -> Literal { + auto &Elems = FoldMap[Ori->Pattern]; FoldExpendedConstraintKey K; - K.Kind = Ori->getFoldOperator(); + K.Kind = Ori->Kind; auto It = llvm::find_if(Elems, [&K](const FoldExpendedConstraintKey &Other) { return K.Kind == Other.Kind; @@ -2557,47 +1960,38 @@ FormulaType SubsumptionChecker::Normalize(const NormalizedConstraint &NC) { AddUniqueClauseToFormula(Res, std::move(C)); }; - switch (NC.getKind()) { - - case NormalizedConstraint::ConstraintKind::Atomic: - return {{find(&static_cast(NC))}}; - - case NormalizedConstraint::ConstraintKind::FoldExpanded: - return {{find(&static_cast(NC))}}; + if (NC.isAtomic()) + return {{find(NC.getAtomicConstraint())}}; - case NormalizedConstraint::ConstraintKind::ConceptId: - return Normalize( - static_cast(NC).getNormalizedConstraint()); + if (NC.isFoldExpanded()) + return {{find(NC.getFoldExpandedConstraint())}}; - 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; - } + FormulaType Left, Right; + SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] { + Left = Normalize(NC.getLHS()); + Right = Normalize(NC.getRHS()); + }); - 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)); - } - } + 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; } + + 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)); + } } + return Res; } void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) { @@ -2612,12 +2006,12 @@ std::optional SubsumptionChecker::Subsumes( const NamedDecl *DP, ArrayRef P, const NamedDecl *DQ, ArrayRef Q) { const NormalizedConstraint *PNormalized = - SemaRef.getNormalizedAssociatedConstraints(DP, P); + getNormalizedAssociatedConstraints(SemaRef, DP, P); if (!PNormalized) return std::nullopt; const NormalizedConstraint *QNormalized = - SemaRef.getNormalizedAssociatedConstraints(DQ, Q); + getNormalizedAssociatedConstraints(SemaRef, DQ, Q); if (!QNormalized) return std::nullopt; @@ -2667,9 +2061,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->getFoldOperator() == B->getFoldOperator() && + A->Kind == B->Kind && FoldExpandedConstraint::AreCompatibleForSubsumption(*A, *B) && - Subsumes(&A->getNormalizedPattern(), &B->getNormalizedPattern()); + Subsumes(&A->Constraint, &B->Constraint); 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 c5724d7f3285e..1131e1f033b72 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17876,15 +17876,13 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, findFailedBooleanCondition(Converted.get()); if (const auto *ConceptIDExpr = dyn_cast_or_null(InnerCond)) { - const ASTConstraintSatisfaction &Satisfaction = - ConceptIDExpr->getSatisfaction(); - if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) { - Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed) - << !HasMessage << Msg.str() << AssertExpr->getSourceRange(); - // Drill down into concept specialization expressions to see why they - // weren't satisfied. - DiagnoseUnsatisfiedConstraint(ConceptIDExpr); - } + // Drill down into concept specialization expressions to see why they + // weren't satisfied. + Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed) + << !HasMessage << Msg.str() << AssertExpr->getSourceRange(); + ConstraintSatisfaction Satisfaction; + if (!CheckConstraintSatisfaction(ConceptIDExpr, Satisfaction)) + DiagnoseUnsatisfiedConstraint(Satisfaction); } 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 ce5527fd9ae59..779ccf5f1e888 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7931,27 +7931,21 @@ Sema::BuildExprRequirement( // be satisfied. TemplateParameterList *TPL = ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); - QualType MatchedType = Context.getReferenceQualifiedType(E); + QualType MatchedType = + Context.getReferenceQualifiedType(E).getCanonicalType(); llvm::SmallVector Args; Args.push_back(TemplateArgument(MatchedType)); auto *Param = cast(TPL->getParam(0)); - MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/true); + MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false); 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); - bool HasError = Constraint.isInvalid(); - if (!HasError) { - SubstitutedConstraintExpr = - cast(Constraint.get()); - if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors) - HasError = true; - } - if (HasError) { + if (Constraint.isInvalid()) { return new (Context) concepts::ExprRequirement( createSubstDiagAt(IDC->getExprLoc(), [&](llvm::raw_ostream &OS) { @@ -7960,6 +7954,8 @@ 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 f741e8283761d..ea5c4265d736d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -12739,8 +12739,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, << (unsigned)FnKindPair.first << (unsigned)ocs_non_template << FnDesc /* Ignored */; ConstraintSatisfaction Satisfaction; - if (S.CheckFunctionConstraints(Fn, Satisfaction, SourceLocation(), - /*ForOverloadResolution=*/true)) + if (S.CheckFunctionConstraints(Fn, Satisfaction)) break; S.DiagnoseUnsatisfiedConstraint(Satisfaction); } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index dcf2876af81fc..2bf1511c5cfa0 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -9,7 +9,6 @@ //===----------------------------------------------------------------------===// #include "TreeTransform.h" -#include "clang/AST/ASTConcept.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -1223,9 +1222,8 @@ static ExprResult formImmediatelyDeclaredConstraint( if (auto *CD = dyn_cast(NamedConcept)) { ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId( SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, - /*FoundDecl=*/FoundDecl ? FoundDecl : CD, CD, &ConstraintArgs, - /*DoCheckConstraintSatisfaction=*/ - !S.inParameterMappingSubstitution()); + /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD, + &ConstraintArgs); } // We have a template template parameter else { @@ -4852,11 +4850,13 @@ void Sema::diagnoseMissingTemplateArguments(const CXXScopeSpec &SS, diagnoseMissingTemplateArguments(Name, Loc); } -ExprResult Sema::CheckConceptTemplateId( - const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl, - TemplateDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs, - bool DoCheckConstraintSatisfaction) { +ExprResult +Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, + const DeclarationNameInfo &ConceptNameInfo, + NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs) { assert(NamedConcept && "A concept template id without a template?"); if (NamedConcept->isInvalidDecl()) @@ -4873,48 +4873,33 @@ ExprResult Sema::CheckConceptTemplateId( DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc()); - // There's a bug with CTAI.CanonicalConverted. - // If the template argument contains a DependentDecltypeType that includes a - // TypeAliasType, and the same written type had occurred previously in the - // source, then the DependentDecltypeType would be canonicalized to that - // previous type which would mess up the substitution. - // FIXME: Reland https://github.com/llvm/llvm-project/pull/101782 properly! auto *CSD = ImplicitConceptSpecializationDecl::Create( Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(), - CTAI.SugaredConverted); + CTAI.CanonicalConverted); ConstraintSatisfaction Satisfaction; bool AreArgsDependent = TemplateSpecializationType::anyDependentTemplateArguments( - *TemplateArgs, CTAI.SugaredConverted); - MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.SugaredConverted, + *TemplateArgs, CTAI.CanonicalConverted); + MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.CanonicalConverted, /*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( 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); } @@ -5232,11 +5217,10 @@ bool Sema::CheckTemplateTypeArgument( } default: { // We allow instantiating a template with template argument packs when - // building deduction guides or mapping constraint template parameters. + // building deduction guides. if (Arg.getKind() == TemplateArgument::Pack && - (CodeSynthesisContexts.back().Kind == - Sema::CodeSynthesisContext::BuildingDeductionGuides || - inParameterMappingSubstitution())) { + CodeSynthesisContexts.back().Kind == + Sema::CodeSynthesisContext::BuildingDeductionGuides) { SugaredConverted.push_back(Arg); CanonicalConverted.push_back(Arg); return false; @@ -5829,20 +5813,6 @@ 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; @@ -5852,6 +5822,8 @@ bool Sema::CheckTemplateArgumentList( // template. TemplateArgumentListInfo NewArgs = TemplateArgs; + TemplateParameterList *Params = GetTemplateParameterList(Template); + SourceLocation RAngleLoc = NewArgs.getRAngleLoc(); // C++23 [temp.arg.general]p1: @@ -6191,12 +6163,11 @@ bool Sema::CheckTemplateArgumentList( CXXThisScopeRAII Scope(*this, RD, ThisQuals, RD != nullptr); MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( - Template, NewContext, /*Final=*/true, CTAI.SugaredConverted, + Template, NewContext, /*Final=*/false, CTAI.CanonicalConverted, /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConceptInstantiation=*/true); - if (!isa(Template) && - EnsureTemplateArgumentListConstraints( + if (EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { if (ConstraintsNotSatisfied) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 6bba505ece07d..f6ee7452c2f9a 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -3206,7 +3206,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 = SugaredDeducedArgs; + Innermost = CanonicalDeducedArgs; MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( Template, Template->getDeclContext(), /*Final=*/false, Innermost, @@ -3218,7 +3218,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, SugaredDeducedArgs); + MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs); if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, Info.getLocation(), @@ -3995,12 +3995,11 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (CheckFunctionTemplateConstraints( Info.getLocation(), FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(), - CTAI.SugaredConverted, Info.AssociatedConstraintsSatisfaction)) + CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction)) return TemplateDeductionResult::MiscellaneousDeductionFailure; if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { - Info.reset( - TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted), - Info.takeCanonical()); + Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy( + Context, CTAI.CanonicalConverted)); return TemplateDeductionResult::ConstraintsNotSatisfied; } } @@ -5168,8 +5167,8 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, CTAI)) return true; - MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.SugaredConverted, - /*Final=*/true); + MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.CanonicalConverted, + /*Final=*/false); // Build up an EvaluationContext with an ImplicitConceptSpecializationDecl so // that the template arguments of the constraint can be preserved. For // example: @@ -5183,7 +5182,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, S, Sema::ExpressionEvaluationContext::Unevaluated, ImplicitConceptSpecializationDecl::Create( S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(), - CTAI.SugaredConverted)); + CTAI.CanonicalConverted)); if (S.CheckConstraintSatisfaction( Concept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL, TypeLoc.getLocalSourceRange(), Satisfaction)) @@ -6677,11 +6676,10 @@ namespace { struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor { llvm::SmallBitVector &Used; unsigned Depth; - bool VisitDeclRefTypes = true; - MarkUsedTemplateParameterVisitor(llvm::SmallBitVector &Used, unsigned Depth, - bool VisitDeclRefTypes = true) - : Used(Used), Depth(Depth), VisitDeclRefTypes(VisitDeclRefTypes) {} + MarkUsedTemplateParameterVisitor(llvm::SmallBitVector &Used, + unsigned Depth) + : Used(Used), Depth(Depth) { } bool VisitTemplateTypeParmType(TemplateTypeParmType *T) override { if (T->getDepth() == Depth) @@ -6702,8 +6700,6 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor { if (auto *NTTP = dyn_cast(E->getDecl())) if (NTTP->getDepth() == Depth) Used[NTTP->getIndex()] = true; - if (VisitDeclRefTypes) - DynamicRecursiveASTVisitor::TraverseType(E->getType()); return true; } @@ -7047,13 +7043,10 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, break; case Type::UnaryTransform: - if (!OnlyDeduced) { - auto *UTT = cast(T); - auto Next = UTT->getUnderlyingType(); - if (Next.isNull()) - Next = UTT->getBaseType(); - MarkUsedTemplateParameters(Ctx, Next, OnlyDeduced, Depth, Used); - } + if (!OnlyDeduced) + MarkUsedTemplateParameters(Ctx, + cast(T)->getUnderlyingType(), + OnlyDeduced, Depth, Used); break; case Type::PackExpansion: @@ -7153,12 +7146,6 @@ Sema::MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced, ::MarkUsedTemplateParameters(Context, E, OnlyDeduced, Depth, Used); } -void Sema::MarkUsedTemplateParametersForSubsumptionParameterMapping( - const Expr *E, unsigned Depth, llvm::SmallBitVector &Used) { - MarkUsedTemplateParameterVisitor(Used, Depth, /*VisitDeclRefTypes=*/false) - .TraverseStmt(const_cast(E)); -} - void Sema::MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs, bool OnlyDeduced, unsigned Depth, @@ -7184,14 +7171,6 @@ 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 b996efd441799..f1c9c5c868159 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -628,14 +628,9 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Inst.InstantiationRange = InstantiationRange; Inst.InConstraintSubstitution = Inst.Kind == CodeSynthesisContext::ConstraintSubstitution; - Inst.InParameterMappingSubstitution = - Inst.Kind == CodeSynthesisContext::ParameterMappingSubstitution; - if (!SemaRef.CodeSynthesisContexts.empty()) { + if (!SemaRef.CodeSynthesisContexts.empty()) Inst.InConstraintSubstitution |= SemaRef.CodeSynthesisContexts.back().InConstraintSubstitution; - Inst.InParameterMappingSubstitution |= - SemaRef.CodeSynthesisContexts.back().InParameterMappingSubstitution; - } Invalid = SemaRef.pushCodeSynthesisContext(Inst); if (!Invalid) { @@ -1380,7 +1375,6 @@ std::optional Sema::isSFINAEContext() const { // Template Instantiation for Types //===----------------------------------------------------------------------===/ namespace { - class TemplateInstantiator : public TreeTransform { const MultiLevelTemplateArgumentList &TemplateArgs; SourceLocation Loc; @@ -1393,11 +1387,7 @@ namespace { // Whether an incomplete substituion should be treated as an error. bool BailOutOnIncomplete; - // Whether to rebuild pack expansion types; We don't do that when - // rebuilding the parameter mapping of a fold expression appearing - // in a constraint expression. - bool BuildPackExpansionTypes = true; - + private: // CWG2770: Function parameters should be instantiated when they are // needed by a satisfaction check of an atomic constraint or // (recursively) by another function parameter. @@ -1420,17 +1410,6 @@ namespace { 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), - BuildPackExpansionTypes(BuildPackExpansionTypes) {} - /// Determine whether the given type \p T has already been /// transformed. /// @@ -1465,8 +1444,7 @@ namespace { bool &ShouldExpand, bool &RetainExpansion, UnsignedOrNone &NumExpansions) { if (SemaRef.CurrentInstantiationScope && - (SemaRef.inConstraintSubstitution() || - SemaRef.inParameterMappingSubstitution())) { + SemaRef.inConstraintSubstitution()) { for (UnexpandedParameterPack ParmPack : Unexpanded) { NamedDecl *VD = ParmPack.first.dyn_cast(); if (auto *PVD = dyn_cast_if_present(VD); @@ -1487,10 +1465,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)) { @@ -1510,10 +1488,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); @@ -1530,9 +1508,9 @@ namespace { std::move(New); return Old; } - void RememberSubstitution(MultiLevelTemplateArgumentList Old) { - const_cast(this->TemplateArgs) = Old; + const_cast(this->TemplateArgs) = + std::move(Old); } TemplateArgument @@ -1713,24 +1691,6 @@ 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 (BuildPackExpansionTypes) - return inherited::RebuildPackExpansion(Pattern, EllipsisLoc, - NumExpansions); - return Pattern; - } - using TreeTransform::TransformTemplateSpecializationType; QualType TransformTemplateSpecializationType(TypeLocBuilder &TLB, @@ -2001,8 +1961,7 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { if (ParmVarDecl *PVD = dyn_cast(D); PVD && SemaRef.CurrentInstantiationScope && - (SemaRef.inConstraintSubstitution() || - SemaRef.inParameterMappingSubstitution()) && + SemaRef.inConstraintSubstitution() && maybeInstantiateFunctionParameterToScope(PVD)) return nullptr; @@ -2800,30 +2759,18 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) { concepts::NestedRequirement * TemplateInstantiator::TransformNestedRequirement( concepts::NestedRequirement *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->isDependent() && !AlwaysRebuild()) + return Req; 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()) @@ -2836,45 +2783,45 @@ TemplateInstantiator::TransformNestedRequirement( SemaRef.Context, TransConstraint.get(), Satisfaction); } - bool Success; - Expr *NewConstraint; - TemplateDeductionInfo Info(Constraint->getBeginLoc()); + ExprResult TransConstraint; + ConstraintSatisfaction Satisfaction; + TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc()); { EnterExpressionEvaluationContext ContextRAII( SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); - - Sema::InstantiatingTemplate ConstrInst( - SemaRef, Constraint->getBeginLoc(), Req, - Sema::InstantiatingTemplate::ConstraintsCheck(), - Constraint->getSourceRange()); - + Sema::SFINAETrap Trap(SemaRef); + Sema::InstantiatingTemplate ConstrInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, Info, + Req->getConstraintExpr()->getSourceRange()); if (ConstrInst.isInvalid()) return nullptr; - - 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."); + 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 " + "by CheckConstraintSatisfaction."); } - - 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; + ASTContext &C = SemaRef.Context; + if (TransConstraint.isUsable() && + TransConstraint.get()->isInstantiationDependent()) + return new (C) concepts::NestedRequirement(TransConstraint.get()); + if (TransConstraint.isInvalid() || !TransConstraint.get() || + Satisfaction.HasSubstitutionFailure()) { + SmallString<128> Entity; + llvm::raw_svector_ostream OS(Entity); + Req->getConstraintExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + return new (C) concepts::NestedRequirement( + SemaRef.Context, C.backupStr(Entity), Satisfaction); } - return new (C) concepts::NestedRequirement(C, NewConstraint, Satisfaction); + return new (C) + concepts::NestedRequirement(C, TransConstraint.get(), Satisfaction); } TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, @@ -3131,7 +3078,7 @@ bool Sema::SubstTypeConstraint( const ASTTemplateArgumentListInfo *TemplArgInfo = TC->getTemplateArgsAsWritten(); - if (!EvaluateConstraints && !inParameterMappingSubstitution()) { + if (!EvaluateConstraints) { UnsignedOrNone Index = TC->getArgPackSubstIndex(); if (!Index) Index = SemaRef.ArgPackSubstIndex; @@ -4431,16 +4378,6 @@ 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 51b55b82f4208..6967301483361 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3722,6 +3722,10 @@ 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, @@ -5106,13 +5110,9 @@ 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()), *PackOutput, + PackLocIterator(*this, In.getArgument().pack_end()), Outputs, Uneval)) return true; @@ -5179,6 +5179,7 @@ bool TreeTransform::TransformTemplateArguments( } return false; + } // FIXME: Find ways to reduce code duplication for pack expansions. @@ -6246,7 +6247,7 @@ ParmVarDecl *TreeTransform::TransformFunctionTypeParam( /* DefArg */ nullptr); newParm->setScopeInfo(OldParm->getFunctionScopeDepth(), OldParm->getFunctionScopeIndex() + indexAdjustment); - getDerived().transformedLocalDecl(OldParm, {newParm}); + transformedLocalDecl(OldParm, {newParm}); return newParm; } @@ -7081,11 +7082,11 @@ QualType TreeTransform::TransformUnaryTransformType( TypeLocBuilder &TLB, UnaryTransformTypeLoc TL) { QualType Result = TL.getType(); - TypeSourceInfo *NewBaseTSI = TL.getUnderlyingTInfo(); if (Result->isDependentType()) { const UnaryTransformType *T = TL.getTypePtr(); - NewBaseTSI = getDerived().TransformType(TL.getUnderlyingTInfo()); + TypeSourceInfo *NewBaseTSI = + getDerived().TransformType(TL.getUnderlyingTInfo()); if (!NewBaseTSI) return QualType(); QualType NewBase = NewBaseTSI->getType(); @@ -7100,7 +7101,7 @@ QualType TreeTransform::TransformUnaryTransformType( UnaryTransformTypeLoc NewTL = TLB.push(Result); NewTL.setKWLoc(TL.getKWLoc()); NewTL.setParensRange(TL.getParensRange()); - NewTL.setUnderlyingTInfo(NewBaseTSI); + NewTL.setUnderlyingTInfo(TL.getUnderlyingTInfo()); return Result; } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 5456e73956659..cf32d4f56b7c2 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=*/false)); + Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/true)); D->setTemplateArguments(Args); } diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index eef97a8588f0b..70b898a53fcbd 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -807,19 +807,15 @@ readConstraintSatisfaction(ASTRecordReader &Record) { if (!Satisfaction.IsSatisfied) { unsigned NumDetailRecords = Record.readInt(); for (unsigned i = 0; i != NumDetailRecords; ++i) { - auto Kind = Record.readInt(); - if (Kind == 0) { + if (/* IsDiagnostic */Record.readInt()) { SourceLocation DiagLocation = Record.readSourceLocation(); StringRef DiagMessage = C.backupStr(Record.readString()); - Satisfaction.Details.emplace_back(new ( - C) ConstraintSubstitutionDiagnostic(DiagLocation, DiagMessage)); - } else if (Kind == 1) { + Satisfaction.Details.emplace_back( + new (C) ConstraintSatisfaction::SubstitutionDiagnostic( + DiagLocation, DiagMessage)); + } else 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 acf345392aa1a..ebda91e3819c3 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -482,20 +482,14 @@ addConstraintSatisfaction(ASTRecordWriter &Record, if (!Satisfaction.IsSatisfied) { Record.push_back(Satisfaction.NumRecords); for (const auto &DetailRecord : Satisfaction) { - if (auto *Diag = dyn_cast( - DetailRecord)) { - Record.push_back(/*Kind=*/0); + auto *E = dyn_cast(DetailRecord); + Record.push_back(/* IsDiagnostic */ E == nullptr); + if (E) + Record.AddStmt(E); + else { + auto *Diag = cast *>(DetailRecord); 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 9419dba057a4e..84d981d2ab8de 100644 --- a/clang/test/AST/ast-dump-concepts.cpp +++ b/clang/test/AST/ast-dump-concepts.cpp @@ -20,9 +20,8 @@ struct Foo { // CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'binary_concept' // CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'binary_concept' // CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} col:9 - // CHECK-NEXT: | |-TemplateArgument type 'R' - // CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0 - // CHECK-NEXT: | | `-TemplateTypeParm {{.*}} 'R' + // CHECK-NEXT: | |-TemplateArgument type 'type-parameter-1-0' + // CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0 // CHECK-NEXT: | `-TemplateArgument type 'int' // CHECK-NEXT: | `-BuiltinType {{.*}} 'int' // CHECK-NEXT: |-TemplateArgument {{.*}} type 'R' @@ -36,9 +35,8 @@ struct Foo { // CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'unary_concept' // CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} 'bool' // CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} col:9 - // CHECK-NEXT: | `-TemplateArgument type 'R' - // CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0 - // CHECK-NEXT: | `-TemplateTypeParm {{.*}} 'R' + // CHECK-NEXT: | `-TemplateArgument type 'type-parameter-1-0' + // CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0 template Foo(R); diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp index 9a3adbcb534e8..781fb9f28cb8d 100644 --- a/clang/test/AST/ast-dump-ctad-alias.cpp +++ b/clang/test/AST/ast-dump-ctad-alias.cpp @@ -185,18 +185,17 @@ void foo() { // CHECK-NEXT: | |-BinaryOperator {{.*}} 'bool' '&&' // CHECK-NEXT: | | |-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'invocable' // CHECK-NEXT: | | | |-ImplicitConceptSpecializationDecl {{.*}} -// 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: | | | | |-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: | | | | | `-ClassTemplateDecl {{.*}} Packs -// CHECK-NEXT: | | | | `-TemplateArgument type 'Ts...' -// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'Ts...' dependent -// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack -// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'Ts' +// CHECK-NEXT: | | | | `-TemplateArgument 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 '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 0e0fc735c6843..5c2948f67d0ee 100644 --- a/clang/test/CXX/drs/cwg25xx.cpp +++ b/clang/test/CXX/drs/cwg25xx.cpp @@ -243,20 +243,19 @@ 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) { // #cwg2565-expr + concept ErrorRequires = requires (ErrorRequires auto x) { // 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@-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}} + // 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}} template concept NestedErrorInRequires = requires (T x) { // #cwg2565-NEIR - requires requires (NestedErrorInRequires auto y) { // #cwg2565-NEIR-inner + requires requires (NestedErrorInRequires auto y) { // 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}} @@ -264,9 +263,8 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07 }; }; static_assert(NestedErrorInRequires); - // 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}} + // 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}} #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 af2fc938fbea2..28b5d0adcf054 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,8 +140,7 @@ concept C7 = sizeof(T) == 1 || sizeof( ::type) == 1; static_assert(!C6); -static_assert(!C6); -// expected-note@-1 {{while checking the satisfaction of concept 'C6' requested here}} +static_assert(!C6); // expected-note{{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 af2dce81d8a4b..31587a956b8ab 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 'class 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 '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 = class nonexistent]}} +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = 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 'same_as' evaluated to false}} + // expected-note@-1{{because type constraint 'same_as' was not satisfied:}} // 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 'T2' does not satisfy 'C2'}} + template struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::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 = T2]}} + using c2c2 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::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 70a96bed05867..033ae349a02e5 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,10 +43,11 @@ 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{{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}} + // 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}} }; static_assert(C2); // expected-error{{static assertion failed}} // expected-note@-1{{while checking the satisfaction of concept 'C2' requested here}} @@ -83,26 +84,31 @@ 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) { +void foo1() requires requires (T x) { // #foo1 requires - True + True // #foo1Value && True; } {} template void fooPipes() requires Pipes {} -template void fooAmps1() requires Amps1 {} +template void fooAmps1() requires Amps1 {} // #fooAmps1 void foo() { foo1(); - 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]}} fooPipes(); fooPipes(); fooAmps1(); - 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}} } template @@ -152,16 +158,15 @@ 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 5dcb1880ded48..5199708cd8166 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 'class nonexistent'}} +// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type '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 = class nonexistent]}} +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = 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 28dff336d053c..5433cfb21955d 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 'has_type' does not satisfy 'C1'}} + // expected-note@-2 {{because 'std_example::has_type' does not satisfy 'C1'}} template struct C2_check {}; - // expected-note@-1 {{because 'has_inner' does not satisfy 'C2'}} + // expected-note@-1 {{because 'std_example::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 = has_type]}} - using c3 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = has_inner]}} + 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 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 'T1' does not satisfy 'C'}} +// expected-note@-1 {{because 'PR48656::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 6dea0c62fe686..59e6a48e48878 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,8 +28,9 @@ 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{{while checking the satisfaction of nested requirement}} - // expected-note@-5{{while substituting template arguments}} + // expected-note@-3{{in instantiation of requirement}} + // expected-note@-4{{while checking the satisfaction}} + // expected-note@-6{{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 34c5c5d338bfe..3992835c44402 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,31 +1,21 @@ // RUN: %clang_cc1 -std=c++2a -x c++ -verify %s -// RUN: %clang_cc1 -std=c++2c -x c++ -verify %s template concept True = true; -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 Foo = True; +template concept Bar = Foo; +template requires Bar struct S { }; +template requires Bar && true struct S { }; template concept True2 = sizeof(T) >= 0; -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 +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 { }; // expected-note@-1{{template is declared here}} -template requires Bar2 && true struct S2 { }; // #SpecS2_2 +template requires Bar2 && true struct S2 { }; // 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 'T &'}} - +// expected-note@-2{{while calculating associated constraint of template 'S2' here}} namespace type_pack { template @@ -81,31 +71,16 @@ 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 // #S3_Header +template requires true struct S3; // expected-note {{template is declared here}} 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}} - +requires true struct S3; // expected-error {{class template partial specialization is not more specialized than the primary template}} // Same as above, for the second position (but this was already working). -template // #S4_Header -requires true struct S4; // #S4 +template +requires true struct S4; // expected-note {{template is declared here}} 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}} - - +requires true struct S4; // expected-error {{class template partial specialization is not more specialized than the primary template}} struct X { template struct Y { @@ -121,7 +96,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 c0406f88db5f3..4f5fdd3b4809a 100644 --- a/clang/test/CXX/temp/temp.param/p10-2a.cpp +++ b/clang/test/CXX/temp/temp.param/p10-2a.cpp @@ -86,18 +86,16 @@ using f1 = F; using f2 = F; // expected-error {{constraints not satisfied for alias template 'F' [with T = long]}} template -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}} +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}} template T, OneOf U> // expected-note@-1 2{{because 'OneOf' evaluated to false}} @@ -126,7 +124,6 @@ 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 ce862666aa48f..99a82d96d321b 100644 --- a/clang/test/SemaCXX/cxx23-assume.cpp +++ b/clang/test/SemaCXX/cxx23-assume.cpp @@ -127,12 +127,13 @@ struct F { template constexpr int f5() requires C { return 1; } // expected-note {{while checking the satisfaction}} - // expected-note@-1 {{candidate template ignored}} + // expected-note@-1 {{while substituting template arguments}} + // expected-note@-2 {{candidate template ignored}} template -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}} +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}} 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 6777dc23c44a6..74b3573a0dcaa 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(A, int)' evaluated to false}} + // expected-note@#tpl-address-e{{because '__is_same(tpl_address::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(A, int)' evaluated to false}} + // expected-note@#tpl-address-e{{because '__is_same(tpl_address::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(A, int)' evaluated to false}} + // expected-note@#tpl-address-f{{because '__is_same(tpl_address::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(A, int)' evaluated to false}} + // expected-note@#tpl-address-f{{because '__is_same(tpl_address::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 137f46ee3dc01..4220486d3aed3 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 = (T(), true); -template concept C = A && true; // #C +template concept A = true; +template concept C = A && true; template concept D = A && __is_same(T, int); @@ -40,23 +40,13 @@ 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; }; // #j1 -template requires (C && ... && true) constexpr int j(T...) { return 1; }; // #j2 +template requires (A || ... || true) +constexpr int j(T...) { return 0; }; +template requires (C && ... && true) +constexpr int j(T...) { return 1; }; 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}} @@ -117,7 +107,7 @@ void test() { } namespace substitution { -struct S { + struct S { using type = int; }; @@ -154,69 +144,51 @@ 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 [with T = ]}} - // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} - // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#and1 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and1 {{because substituted constraint expression is ill-formed}} static_assert(And1() == 1); // expected-error {{no matching function for call to 'And1'}} - // 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'}} + // expected-note@#and1 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and1 {{because substituted constraint expression is ill-formed}} static_assert(And1() == 1); // expected-error {{no matching function for call to 'And1'}} - // 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'}} + // expected-note@#and1 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and1 {{because substituted constraint expression is ill-formed}} 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); static_assert(And2() == 2); // expected-error {{no matching function for call to 'And2'}} - // 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'}} + // expected-note@#and2 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and2 {{because substituted constraint expression is ill-formed}} 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 T::type' does not satisfy 'C'}} - // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#and2 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and2 {{because substituted constraint expression is ill-formed}} 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 T::type' does not satisfy 'C'}} - // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#and2 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and2 {{because substituted constraint expression is ill-formed}} 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 [with T = int, U = <>]}} - // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}} - // expected-note@#C {{because 'T' does not satisfy 'A'}} - + // expected-note@#and3 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and3 {{because substituted constraint expression is ill-formed}} static_assert(And3() == 3); // expected-error {{no matching function for call to 'And3'}} - // 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'}} - + // expected-note@#and3 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and3 {{because substituted constraint expression is ill-formed}} static_assert(And3() == 3); // expected-error {{no matching function for call to 'And3'}} - // 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'}} - + // expected-note@#and3 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and3 {{because substituted constraint expression is ill-formed}} static_assert(And3() == 3); // expected-error {{no matching function for call to 'And3'}} - // 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'}} + // expected-note@#and3 {{candidate template ignored: constraints not satisfied}} + // expected-note@#and3 {{because substituted constraint expression is ill-formed}} static_assert(Or1<>() == 1); // expected-error {{no matching function for call to 'Or1'}} @@ -226,26 +198,25 @@ 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 'typename T::type' does not satisfy 'C'}} - // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#or1 {{candidate template ignored: constraints not satisfied}} \ + // expected-note@#or1 {{because substituted constraint expression is ill-formed}} + 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 [with T = int, U = <>]}} - // expected-note@#or2 {{because 'typename T::type' does not satisfy 'C'}} - // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#or2 {{candidate template ignored: constraints not satisfied}} \ + // expected-note@#or2 {{because substituted constraint expression is ill-formed}} + 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 'typename T::type' does not satisfy 'C'}} - // expected-note@#C {{because 'T' does not satisfy 'A'}} + // expected-note@#or3 {{candidate template ignored: constraints not satisfied}} \ + // expected-note@#or3 {{because substituted constraint expression is ill-formed}} } namespace bool_conversion_break { @@ -255,7 +226,7 @@ struct Thingy { static constexpr int compare(const Thingy&) {return 1;} }; template -void f(A *, A *) // expected-note {{candidate template ignored: constraints not satisfied}} +void f(A *, A *) // expected-note {{candidate template ignored: failed template argument deduction}} requires (T::compare(U{}) && ...); // expected-error {{atomic constraint must be of type 'bool' (found 'int')}} void g() { @@ -298,7 +269,9 @@ struct S { static_assert(S::f() == 2); -static_assert(S::g() == 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}} } @@ -411,98 +384,3 @@ 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 4ad3fd95039cd..ed55a059bb53c 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