diff options
Diffstat (limited to 'clang')
56 files changed, 2730 insertions, 1204 deletions
diff --git a/clang/docs/InternalsManual.rst b/clang/docs/InternalsManual.rst index bd74227..c677ddfa 100644 --- a/clang/docs/InternalsManual.rst +++ b/clang/docs/InternalsManual.rst @@ -2859,6 +2859,67 @@ This library is called by the :ref:`Parser library <Parser>` 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 <https://eel.is/c++draft/temp.constr.normal>`_ +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 <typename T> + concept A = __is_same(T, int); + + template <typename U> + concept B = A<U> && __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 145a83a..d2e5bd2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -160,6 +160,10 @@ 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 ^^^^^^^^^^^^^^^^^^^^^ @@ -361,7 +365,7 @@ Bug Fixes in This Version first parameter. (#GH113323). - Fixed a crash with incompatible pointer to integer conversions in designated initializers involving string literals. (#GH154046) -- Fix crash on CTAD for alias template. (#GH131342) +- Fix crash on CTAD for alias template. (#GH131342), (#GH131408) - Clang now emits a frontend error when a function marked with the `flatten` attribute calls another function that requires target features not enabled in the caller. This prevents a fatal error in the backend. diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 72da005..f362f24 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -28,10 +28,20 @@ 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<SourceLocation, StringRef>; +using UnsatisfiedConstraintRecord = + llvm::PointerUnion<const Expr *, const ConceptReference *, + const ConstraintSubstitutionDiagnostic *>; + /// The result of a constraint satisfaction check, containing the necessary /// information to diagnose an unsatisfied constraint. class ConstraintSatisfaction : public llvm::FoldingSetNode { @@ -48,16 +58,13 @@ public: ArrayRef<TemplateArgument> TemplateArgs) : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {} - using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>; - using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>; - 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<Detail, 4> Details; + llvm::SmallVector<UnsatisfiedConstraintRecord, 4> Details; void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) { Profile(ID, C, ConstraintOwner, TemplateArgs); @@ -69,19 +76,12 @@ public: bool HasSubstitutionFailure() { for (const auto &Detail : Details) - if (Detail.dyn_cast<SubstitutionDiagnostic *>()) + if (Detail.dyn_cast<const ConstraintSubstitutionDiagnostic *>()) 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<Expr *, std::pair<SourceLocation, StringRef> *>; - /// \brief The result of a constraint satisfaction check, containing the /// necessary information to diagnose an unsatisfied constraint. /// @@ -101,6 +101,10 @@ struct ASTConstraintSatisfaction final : return getTrailingObjects() + NumRecords; } + ArrayRef<UnsatisfiedConstraintRecord> records() const { + return {begin(), end()}; + } + ASTConstraintSatisfaction(const ASTContext &C, const ConstraintSatisfaction &Satisfaction); ASTConstraintSatisfaction(const ASTContext &C, @@ -282,6 +286,11 @@ public: } }; +/// 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 12351e9..78220d4 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -3877,7 +3877,6 @@ typename clang::LazyGenerationalUpdatePtr<Owner, T, Update>::ValueType return new (Ctx) LazyData(Source, Value); return Value; } - template <> struct llvm::DenseMapInfo<llvm::FoldingSetNodeID> { static FoldingSetNodeID getEmptyKey() { return FoldingSetNodeID{}; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f53aafd..265462a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -65,6 +65,7 @@ #include "clang/Sema/Redeclaration.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaBase.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/TypoCorrection.h" #include "clang/Sema/Weak.h" #include "llvm/ADT/APInt.h" @@ -11694,8 +11695,9 @@ public: ExprResult CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, const DeclarationNameInfo &ConceptNameInfo, - NamedDecl *FoundDecl, ConceptDecl *NamedConcept, - const TemplateArgumentListInfo *TemplateArgs); + NamedDecl *FoundDecl, TemplateDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs, + bool DoCheckConstraintSatisfaction = true); void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc); void diagnoseMissingTemplateArguments(const CXXScopeSpec &SS, @@ -12025,6 +12027,13 @@ public: bool UpdateArgsWithConversions = true, bool *ConstraintsNotSatisfied = nullptr); + bool CheckTemplateArgumentList( + TemplateDecl *Template, TemplateParameterList *Params, + SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs, + const DefaultArguments &DefaultArgs, bool PartialTemplateArgs, + CheckTemplateArgumentInfo &CTAI, bool UpdateArgsWithConversions = true, + bool *ConstraintsNotSatisfied = nullptr); + bool CheckTemplateTypeArgument( TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg, SmallVectorImpl<TemplateArgument> &SugaredConverted, @@ -12783,6 +12792,18 @@ public: 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. /// @@ -12799,6 +12820,9 @@ public: void MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs, unsigned Depth, llvm::SmallBitVector &Used); + void MarkUsedTemplateParameters(ArrayRef<TemplateArgumentLoc> TemplateArgs, + unsigned Depth, llvm::SmallBitVector &Used); + void MarkDeducedTemplateParameters(const FunctionTemplateDecl *FunctionTemplate, llvm::SmallBitVector &Deduced) { @@ -13096,6 +13120,9 @@ public: /// 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; @@ -13146,8 +13173,10 @@ public: CodeSynthesisContext() : Kind(TemplateInstantiation), SavedInNonInstantiationSFINAEContext(false), - InConstraintSubstitution(false), Entity(nullptr), Template(nullptr), - TemplateArgs(nullptr), NumTemplateArgs(0), DeductionInfo(nullptr) {} + InConstraintSubstitution(false), + InParameterMappingSubstitution(false), Entity(nullptr), + Template(nullptr), TemplateArgs(nullptr), NumTemplateArgs(0), + DeductionInfo(nullptr) {} /// Determines whether this template is an actual instantiation /// that should be counted toward the maximum instantiation depth. @@ -13359,6 +13388,11 @@ public: const MultiLevelTemplateArgumentList &TemplateArgs, TemplateArgumentListInfo &Outputs); + bool SubstTemplateArgumentsInParameterMapping( + ArrayRef<TemplateArgumentLoc> 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. /// @@ -13820,6 +13854,12 @@ public: CodeSynthesisContexts.back().InConstraintSubstitution; } + bool inParameterMappingSubstitution() const { + return !CodeSynthesisContexts.empty() && + CodeSynthesisContexts.back().InParameterMappingSubstitution && + !inConstraintSubstitution(); + } + using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>; /// \brief create a Requirement::SubstitutionDiagnostic with only a @@ -14704,6 +14744,10 @@ public: SatisfactionStack.swap(NewSS); } + using ConstrainedDeclOrNestedRequirement = + llvm::PointerUnion<const NamedDecl *, + const concepts::NestedRequirement *>; + /// Check whether the given expression is a valid constraint expression. /// A diagnostic is emitted if it is not, false is returned, and /// PossibleNonPrimary will be set to true if the failure might be due to a @@ -14728,44 +14772,12 @@ public: /// \returns true if an error occurred and satisfaction could not be checked, /// false otherwise. bool CheckConstraintSatisfaction( - const NamedDecl *Template, + ConstrainedDeclOrNestedRequirement Entity, ArrayRef<AssociatedConstraint> AssociatedConstraints, const MultiLevelTemplateArgumentList &TemplateArgLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { - llvm::SmallVector<Expr *, 4> Converted; - return CheckConstraintSatisfaction(Template, AssociatedConstraints, - Converted, TemplateArgLists, - TemplateIDRange, Satisfaction); - } - - /// \brief Check whether the given list of constraint expressions are - /// satisfied (as if in a 'conjunction') given template arguments. - /// Additionally, takes an empty list of Expressions which is populated with - /// the instantiated versions of the ConstraintExprs. - /// \param Template the template-like entity that triggered the constraints - /// check (either a concept or a constrained entity). - /// \param ConstraintExprs a list of constraint expressions, treated as if - /// they were 'AND'ed together. - /// \param ConvertedConstraints a out parameter that will get populated with - /// the instantiated version of the ConstraintExprs if we successfully checked - /// satisfaction. - /// \param TemplateArgList the multi-level list of template arguments to - /// substitute into the constraint expression. This should be relative to the - /// top-level (hence multi-level), since we need to instantiate fully at the - /// time of checking. - /// \param TemplateIDRange The source range of the template id that - /// caused the constraints check. - /// \param Satisfaction if true is returned, will contain details of the - /// satisfaction, with enough information to diagnose an unsatisfied - /// expression. - /// \returns true if an error occurred and satisfaction could not be checked, - /// false otherwise. - bool CheckConstraintSatisfaction( - const NamedDecl *Template, - ArrayRef<AssociatedConstraint> AssociatedConstraints, - llvm::SmallVectorImpl<Expr *> &ConvertedConstraints, - const MultiLevelTemplateArgumentList &TemplateArgList, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction, + const ConceptReference *TopLevelConceptId = nullptr, + Expr **ConvertedExpr = nullptr); /// \brief Check whether the given non-dependent constraint expression is /// satisfied. Returns false and updates Satisfaction with the satisfaction @@ -14831,16 +14843,17 @@ public: /// \param First whether this is the first time an unsatisfied constraint is /// diagnosed for this error. void DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction, + SourceLocation Loc = {}, bool First = true); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. void - DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction, + DiagnoseUnsatisfiedConstraint(const ConceptSpecializationExpr *ConstraintExpr, bool First = true); const NormalizedConstraint *getNormalizedAssociatedConstraints( - const NamedDecl *ConstrainedDecl, + ConstrainedDeclOrNestedRequirement Entity, ArrayRef<AssociatedConstraint> AssociatedConstraints); /// \brief Check whether the given declaration's associated constraints are @@ -14865,6 +14878,15 @@ public: const NamedDecl *D1, ArrayRef<AssociatedConstraint> AC1, const NamedDecl *D2, ArrayRef<AssociatedConstraint> 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<llvm::FoldingSetNodeID, + UnsubstitutedConstraintSatisfactionCacheResult> + 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 @@ -14875,8 +14897,11 @@ private: /// constrained declarations). If an error occurred while normalizing the /// associated constraints of the template or concept, nullptr will be cached /// here. - llvm::DenseMap<const NamedDecl *, NormalizedConstraint *> NormalizationCache; + llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint *> + NormalizationCache; + /// Cache whether the associated constraint of a declaration + /// is satisfied. llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &> SatisfactionCache; diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h index 648a9c5..51ca1e1 100644 --- a/clang/include/clang/Sema/SemaConcept.h +++ b/clang/include/clang/Sema/SemaConcept.h @@ -16,130 +16,406 @@ #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 <optional> #include <utility> namespace clang { class Sema; +class MultiLevelTemplateArgumentList; -enum { ConstraintAlignment = 8 }; +/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is +/// either an atomic constraint, a conjunction of normalized constraints or a +/// disjunction of normalized constraints. +struct NormalizedConstraint { + + enum class ConstraintKind : 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<const Expr *, const ConceptReference *>; + + 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; + } -struct alignas(ConstraintAlignment) AtomicConstraint { - const Expr *ConstraintExpr; - const NamedDecl *ConstraintDecl; - std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping; + llvm::MutableArrayRef<TemplateArgumentLoc> getParameterMapping() const { + return {Atomic.Args, Atomic.Indexes.count()}; + } + + TemplateParameterList *getUsedTemplateParamList() const { + return Atomic.ParamList; + } - AtomicConstraint(const Expr *ConstraintExpr, const NamedDecl *ConstraintDecl) - : ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {}; + void updateParameterMapping(OccurenceList Indexes, + OccurenceList IndexesForSubsumption, + llvm::MutableArrayRef<TemplateArgumentLoc> 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; + } bool hasMatchingParameterMapping(ASTContext &C, - const AtomicConstraint &Other) const { - if (!ParameterMapping != !Other.ParameterMapping) + const NormalizedConstraint &Other) const { + assert(getKind() != ConstraintKind::Compound); + + if (hasParameterMapping() != Other.hasParameterMapping()) return false; - if (!ParameterMapping) + if (!hasParameterMapping()) return true; - if (ParameterMapping->size() != Other.ParameterMapping->size()) - return false; - for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) { + llvm::ArrayRef<TemplateArgumentLoc> ParameterMapping = + getParameterMapping(); + llvm::ArrayRef<TemplateArgumentLoc> OtherParameterMapping = + Other.getParameterMapping(); + + const OccurenceList &Indexes = mappingOccurenceListForSubsumption(); + const OccurenceList &OtherIndexes = + Other.mappingOccurenceListForSubsumption(); + + if (ParameterMapping.size() != OtherParameterMapping.size()) + return false; + for (unsigned I = 0, S = ParameterMapping.size(); I < S; ++I) { + if (Indexes[I] != OtherIndexes[I]) + return false; + if (!Indexes[I]) + continue; llvm::FoldingSetNodeID IDA, IDB; - C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument()) + C.getCanonicalTemplateArgument(ParameterMapping[I].getArgument()) .Profile(IDA, C); - C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument()) + C.getCanonicalTemplateArgument(OtherParameterMapping[I].getArgument()) .Profile(IDB, C); if (IDA != IDB) return false; } return true; } -}; -struct alignas(ConstraintAlignment) NormalizedConstraintPair; -struct alignas(ConstraintAlignment) FoldExpandedConstraint; +public: + ConstraintKind getKind() const { + return static_cast<ConstraintKind>(Atomic.Kind); + } -/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is -/// either an atomic constraint, a conjunction of normalized constraints or a -/// disjunction of normalized constraints. -struct NormalizedConstraint { + SourceLocation getBeginLoc() const { + switch (getKind()) { + case ConstraintKind::Atomic: + return cast<const Expr *>(Atomic.ConstraintExpr)->getBeginLoc(); + case ConstraintKind::ConceptId: + return cast<const ConceptReference *>(Atomic.ConstraintExpr) + ->getBeginLoc(); + case ConstraintKind::Compound: + return Compound.LHS->getBeginLoc(); + case ConstraintKind::FoldExpanded: + return FoldExpanded.Pattern->getBeginLoc(); + } + } + + SourceLocation getEndLoc() const { + switch (getKind()) { + case ConstraintKind::Atomic: + return cast<const Expr *>(Atomic.ConstraintExpr)->getEndLoc(); + case ConstraintKind::ConceptId: + return cast<const ConceptReference *>(Atomic.ConstraintExpr)->getEndLoc(); + case ConstraintKind::Compound: + return Compound.RHS->getEndLoc(); + case ConstraintKind::FoldExpanded: + return FoldExpanded.Pattern->getEndLoc(); + } + } + + SourceRange getSourceRange() const { return {getBeginLoc(), getEndLoc()}; } + +private: friend class Sema; + static NormalizedConstraint * + fromAssociatedConstraints(Sema &S, const NamedDecl *D, + ArrayRef<AssociatedConstraint> ACs); + static NormalizedConstraint *fromConstraintExpr(Sema &S, const NamedDecl *D, + const Expr *E, + UnsignedOrNone SubstIndex); +}; + +class CompoundConstraint : public NormalizedConstraint { + using NormalizedConstraint::NormalizedConstraint; - enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; +public: + static CompoundConstraint *Create(ASTContext &Ctx, NormalizedConstraint *LHS, + CompoundConstraintKind CCK, + NormalizedConstraint *RHS) { + return new (Ctx) CompoundConstraint(LHS, CCK, RHS); + } - using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1, - CompoundConstraintKind>; + static CompoundConstraint *CreateConjunction(ASTContext &Ctx, + NormalizedConstraint *LHS, + NormalizedConstraint *RHS) { + return new (Ctx) CompoundConstraint(LHS, CCK_Conjunction, RHS); + } - llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *, - CompoundConstraint> - Constraint; + const NormalizedConstraint &getLHS() const { return *Compound.LHS; } - NormalizedConstraint(AtomicConstraint *C): Constraint{C} { }; - NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {}; + NormalizedConstraint &getLHS() { return *Compound.LHS; } - NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS, - NormalizedConstraint RHS, CompoundConstraintKind Kind); + const NormalizedConstraint &getRHS() const { return *Compound.RHS; } - NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other); - NormalizedConstraint(NormalizedConstraint &&Other): - Constraint(Other.Constraint) { - Other.Constraint = nullptr; + NormalizedConstraint &getRHS() { return *Compound.RHS; } + + CompoundConstraintKind getCompoundKind() const { + return static_cast<CompoundConstraintKind>(Compound.CCK); } - NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete; - NormalizedConstraint &operator=(NormalizedConstraint &&Other) { - if (&Other != this) { - NormalizedConstraint Temp(std::move(Other)); - std::swap(Constraint, Temp.Constraint); - } - return *this; +}; + +class NormalizedConstraintWithParamMapping : public NormalizedConstraint { +protected: + using NormalizedConstraint::NormalizedConstraint; + +public: + using NormalizedConstraint::getParameterMapping; + using NormalizedConstraint::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); } +}; + +class AtomicConstraint : public NormalizedConstraintWithParamMapping { + using NormalizedConstraintWithParamMapping:: + NormalizedConstraintWithParamMapping; - bool isAtomic() const { return llvm::isa<AtomicConstraint *>(Constraint); } - bool isFoldExpanded() const { - return llvm::isa<FoldExpandedConstraint *>(Constraint); +public: + static AtomicConstraint *Create(ASTContext &Ctx, const Expr *ConstraintExpr, + const NamedDecl *ConstraintDecl, + UnsignedOrNone PackIndex) { + return new (Ctx) + AtomicConstraint(ConstraintExpr, ConstraintDecl, PackIndex); } - bool isCompound() const { return llvm::isa<CompoundConstraint>(Constraint); } - CompoundConstraintKind getCompoundKind() const; + const Expr *getConstraintExpr() const { + return cast<const Expr *>(Atomic.ConstraintExpr); + } +}; - NormalizedConstraint &getLHS() const; - NormalizedConstraint &getRHS() const; +class FoldExpandedConstraint : public NormalizedConstraintWithParamMapping { + using NormalizedConstraintWithParamMapping:: + NormalizedConstraintWithParamMapping; - AtomicConstraint *getAtomicConstraint() const; +public: + static FoldExpandedConstraint *Create(ASTContext &Ctx, const Expr *Pattern, + const NamedDecl *ConstraintDecl, + FoldOperatorKind OpKind, + NormalizedConstraint *Constraint) { + return new (Ctx) + FoldExpandedConstraint(Pattern, OpKind, Constraint, ConstraintDecl); + } - FoldExpandedConstraint *getFoldExpandedConstraint() const; + using NormalizedConstraint::hasMatchingParameterMapping; -private: - static std::optional<NormalizedConstraint> - fromAssociatedConstraints(Sema &S, const NamedDecl *D, - ArrayRef<AssociatedConstraint> ACs); - static std::optional<NormalizedConstraint> - fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E); -}; + FoldOperatorKind getFoldOperator() const { + return static_cast<FoldOperatorKind>(FoldExpanded.FoldOperator); + } -struct alignas(ConstraintAlignment) NormalizedConstraintPair { - NormalizedConstraint LHS, RHS; -}; + const Expr *getPattern() const { return FoldExpanded.Pattern; } -struct alignas(ConstraintAlignment) FoldExpandedConstraint { - enum class FoldOperatorKind { And, Or } Kind; - NormalizedConstraint Constraint; - const Expr *Pattern; + const NormalizedConstraint &getNormalizedPattern() const { + return *FoldExpanded.Constraint; + } - FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C, - const Expr *Pattern) - : Kind(K), Constraint(std::move(C)), Pattern(Pattern) {}; + NormalizedConstraint &getNormalizedPattern() { + return *FoldExpanded.Constraint; + } static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A, const FoldExpandedConstraint &B); }; -const NormalizedConstraint *getNormalizedAssociatedConstraints( - Sema &S, const NamedDecl *ConstrainedDecl, - ArrayRef<AssociatedConstraint> AssociatedConstraints); +class ConceptIdConstraint : public NormalizedConstraintWithParamMapping { + using NormalizedConstraintWithParamMapping:: + NormalizedConstraintWithParamMapping; + +public: + static ConceptIdConstraint * + Create(ASTContext &Ctx, const ConceptReference *ConceptId, + NormalizedConstraint *SubConstraint, const NamedDecl *ConstraintDecl, + 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<const ConceptReference *>(ConceptId.ConstraintExpr); + } + + const NormalizedConstraint &getNormalizedConstraint() const { + return *ConceptId.Sub; + } + + NormalizedConstraint &getNormalizedConstraint() { return *ConceptId.Sub; } +}; + +struct UnsubstitutedConstraintSatisfactionCacheResult { + ExprResult SubstExpr; + ConstraintSatisfaction Satisfaction; +}; /// \brief SubsumptionChecker establishes subsumption /// between two set of constraints. @@ -189,13 +465,13 @@ private: }; struct MappedAtomicConstraint { - AtomicConstraint *Constraint; + const AtomicConstraint *Constraint; Literal ID; }; struct FoldExpendedConstraintKey { FoldExpandedConstraint::FoldOperatorKind Kind; - AtomicConstraint *Constraint; + const AtomicConstraint *Constraint; Literal ID; }; @@ -207,7 +483,7 @@ private: // A map from a literal to a corresponding associated constraint. // We do not have enough bits left for a pointer union here :( - llvm::DenseMap<uint16_t, void *> ReverseMap; + llvm::DenseMap<uint16_t, const void *> ReverseMap; // Fold expanded constraints ask us to recursively establish subsumption. // This caches the result. @@ -234,12 +510,12 @@ private: FormulaType Normalize(const NormalizedConstraint &C); void AddUniqueClauseToFormula(Formula &F, Clause C); - Literal find(AtomicConstraint *); - Literal find(FoldExpandedConstraint *); + Literal find(const AtomicConstraint *); + Literal find(const FoldExpandedConstraint *); uint16_t getNewLiteralId(); }; -} // clang +} // namespace clang #endif // LLVM_CLANG_SEMA_SEMACONCEPT_H diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index 115c19d..60c7d27 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -234,21 +234,25 @@ enum class TemplateSubstitutionKind : char { /// Replaces the current 'innermost' level with the provided argument list. /// This is useful for type deduction cases where we need to get the entire /// list from the AST, but then add the deduced innermost list. - void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) { + void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args, + bool Final = false) { assert((!TemplateArgumentLists.empty() || NumRetainedOuterLevels) && "Replacing in an empty list?"); if (!TemplateArgumentLists.empty()) { - assert((TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() || - TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() == - AssociatedDecl) && - "Trying to change incorrect declaration?"); TemplateArgumentLists[0].Args = Args; - } else { - --NumRetainedOuterLevels; - TemplateArgumentLists.push_back( - {{AssociatedDecl, /*Final=*/false}, Args}); + return; } + --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/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h index c3601a4..f222ded 100644 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -220,7 +220,6 @@ private: std::vector<std::string> VisibleModules; std::vector<Command> Commands; std::string ContextHash; - std::vector<std::string> OutputPaths; const llvm::DenseSet<ModuleID> &AlreadySeen; }; diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index d658890..fd12bc4 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -24,13 +24,18 @@ static void CreateUnsatisfiedConstraintRecord(const ASTContext &C, const UnsatisfiedConstraintRecord &Detail, UnsatisfiedConstraintRecord *TrailingObject) { - if (auto *E = dyn_cast<Expr *>(Detail)) + if (Detail.isNull()) + new (TrailingObject) UnsatisfiedConstraintRecord(nullptr); + else if (const auto *E = llvm::dyn_cast<const Expr *>(Detail)) new (TrailingObject) UnsatisfiedConstraintRecord(E); + else if (const auto *Concept = + llvm::dyn_cast<const ConceptReference *>(Detail)) + new (TrailingObject) UnsatisfiedConstraintRecord(Concept); else { auto &SubstitutionDiagnostic = - *cast<std::pair<SourceLocation, StringRef> *>(Detail); + *cast<const clang::ConstraintSubstitutionDiagnostic *>(Detail); StringRef Message = C.backupStr(SubstitutionDiagnostic.second); - auto *NewSubstDiag = new (C) std::pair<SourceLocation, StringRef>( + auto *NewSubstDiag = new (C) clang::ConstraintSubstitutionDiagnostic( SubstitutionDiagnostic.first, Message); new (TrailingObject) UnsatisfiedConstraintRecord(NewSubstDiag); } @@ -74,9 +79,10 @@ ASTConstraintSatisfaction *ASTConstraintSatisfaction::Rebuild( return new (Mem) ASTConstraintSatisfaction(C, Satisfaction); } -void ConstraintSatisfaction::Profile( - llvm::FoldingSetNodeID &ID, const ASTContext &C, - const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> TemplateArgs) { +void ConstraintSatisfaction::Profile(llvm::FoldingSetNodeID &ID, + const ASTContext &C, + const NamedDecl *ConstraintOwner, + ArrayRef<TemplateArgument> TemplateArgs) { ID.AddPointer(ConstraintOwner); ID.AddInteger(TemplateArgs.size()); for (auto &Arg : TemplateArgs) @@ -116,6 +122,19 @@ void ConceptReference::print(llvm::raw_ostream &OS, } } +const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB, + const ConceptReference *C) { + std::string NameStr; + llvm::raw_string_ostream OS(NameStr); + LangOptions LO; + LO.CPlusPlus = true; + LO.Bool = true; + OS << '\''; + C->print(OS, PrintingPolicy(LO)); + OS << '\''; + return DB << NameStr; +} + concepts::ExprRequirement::ExprRequirement( Expr *E, bool IsSimple, SourceLocation NoexceptLoc, ReturnTypeRequirement Req, SatisfactionStatus Status, diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 1c8fd83..f43fa8c 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 (Expr *E = Record->dyn_cast<Expr *>()) { + if (const Expr *E = Record->dyn_cast<const Expr *>()) { ExpectedExpr ToSecondExpr = import(E); if (!ToSecondExpr) return ToSecondExpr.takeError(); ToSat.Details.emplace_back(ToSecondExpr.get()); } else { - auto Pair = Record->dyn_cast<std::pair<SourceLocation, StringRef> *>(); + auto Pair = + Record->dyn_cast<const ConstraintSubstitutionDiagnostic *>(); ExpectedSLoc ToPairFirst = import(Pair->first); if (!ToPairFirst) return ToPairFirst.takeError(); StringRef ToPairSecond = ImportASTStringRef(Pair->second); - ToSat.Details.emplace_back( - new (Importer.getToContext()) - ConstraintSatisfaction::SubstitutionDiagnostic{ - ToPairFirst.get(), ToPairSecond}); + ToSat.Details.emplace_back(new (Importer.getToContext()) + ConstraintSubstitutionDiagnostic{ + ToPairFirst.get(), ToPairSecond}); } } } diff --git a/clang/lib/CodeGen/Targets/X86.cpp b/clang/lib/CodeGen/Targets/X86.cpp index c03ba94..fb78948 100644 --- a/clang/lib/CodeGen/Targets/X86.cpp +++ b/clang/lib/CodeGen/Targets/X86.cpp @@ -1343,9 +1343,10 @@ class X86_64ABIInfo : public ABIInfo { } bool returnCXXRecordGreaterThan128InMem() const { - // Clang <= 20.0 did not do this. + // Clang <= 20.0 did not do this, and PlayStation does not do this. if (getContext().getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver20) + LangOptions::ClangABI::Ver20 || + getTarget().getTriple().isPS()) return false; return true; diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index dc6d232..8413090 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -12,9 +12,11 @@ #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" @@ -27,7 +29,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringExtras.h" -#include <optional> +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace sema; @@ -85,7 +87,7 @@ public: OK_Ordinary, Loc, FPOptionsOverride{}); } }; -} +} // namespace bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, Token NextToken, bool *PossibleNonPrimary, @@ -146,14 +148,14 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, if (!Context.hasSameUnqualifiedType(Type, Context.BoolTy)) { Diag(ConstraintExpression->getExprLoc(), - diag::err_non_bool_atomic_constraint) << Type - << ConstraintExpression->getSourceRange(); + diag::err_non_bool_atomic_constraint) + << Type << ConstraintExpression->getSourceRange(); CheckForNonPrimary(); return false; } if (PossibleNonPrimary) - *PossibleNonPrimary = false; + *PossibleNonPrimary = false; return true; } @@ -164,52 +166,315 @@ struct SatisfactionStackRAII { SatisfactionStackRAII(Sema &SemaRef, const NamedDecl *ND, const llvm::FoldingSetNodeID &FSNID) : SemaRef(SemaRef) { - if (ND) { + if (ND) { SemaRef.PushSatisfactionStackEntry(ND, FSNID); Inserted = true; - } + } } ~SatisfactionStackRAII() { - if (Inserted) - SemaRef.PopSatisfactionStackEntry(); + if (Inserted) + SemaRef.PopSatisfactionStackEntry(); } }; } // namespace -static bool -DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID, - const NamedDecl *Templ, const Expr *E, - const MultiLevelTemplateArgumentList &MLTAL) { +static bool DiagRecursiveConstraintEval( + Sema &S, llvm::FoldingSetNodeID &ID, const NamedDecl *Templ, const Expr *E, + const MultiLevelTemplateArgumentList *MLTAL = nullptr) { E->Profile(ID, S.Context, /*Canonical=*/true); - for (const auto &List : MLTAL) - for (const auto &TemplateArg : List.Args) - TemplateArg.Profile(ID, S.Context); - - // Note that we have to do this with our own collection, because there are - // times where a constraint-expression check can cause us to need to evaluate - // other constriants that are unrelated, such as when evaluating a recovery - // expression, or when trying to determine the constexpr-ness of special - // members. Otherwise we could just use the - // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function. + if (MLTAL) { + for (const auto &List : *MLTAL) + for (const auto &TemplateArg : List.Args) + S.Context.getCanonicalTemplateArgument(TemplateArg) + .Profile(ID, S.Context); + } if (S.SatisfactionStackContains(Templ, ID)) { S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self) << E << E->getSourceRange(); return true; } - return false; } -static ExprResult EvaluateAtomicConstraint( - Sema &S, const Expr *AtomicExpr, const NamedDecl *Template, - SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, - ConstraintSatisfaction &Satisfaction) { +// Figure out the to-translation-unit depth for this function declaration for +// the purpose of seeing if they differ by constraints. This isn't the same as +// getTemplateDepth, because it includes already instantiated parents. +static unsigned +CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND, + bool SkipForSpecialization = false) { + MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( + ND, ND->getLexicalDeclContext(), /*Final=*/false, + /*Innermost=*/std::nullopt, + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true, SkipForSpecialization); + return MLTAL.getNumLevels(); +} + +namespace { +class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> { + unsigned TemplateDepth = 0; + +public: + using inherited = TreeTransform<AdjustConstraintDepth>; + AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth) + : inherited(SemaRef), TemplateDepth(TemplateDepth) {} + + using inherited::TransformTemplateTypeParmType; + QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB, + TemplateTypeParmTypeLoc TL, bool) { + const TemplateTypeParmType *T = TL.getTypePtr(); + + TemplateTypeParmDecl *NewTTPDecl = nullptr; + if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl()) + NewTTPDecl = cast_or_null<TemplateTypeParmDecl>( + TransformDecl(TL.getNameLoc(), OldTTPDecl)); + + QualType Result = getSema().Context.getTemplateTypeParmType( + T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(), + NewTTPDecl); + TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; + } + + 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<HashParameterMapping> { + using inherited = RecursiveASTVisitor<HashParameterMapping>; + friend inherited; + + Sema &SemaRef; + const MultiLevelTemplateArgumentList &TemplateArgs; + llvm::FoldingSetNodeID &ID; + llvm::SmallVector<TemplateArgument, 10> 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<NonTypeTemplateParmDecl>(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<ValueDecl>(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<UnsignedOrNone> _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<TemplateArgumentLoc> 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<MultiLevelTemplateArgumentList> SubstitutionInTemplateArguments( + const NormalizedConstraintWithParamMapping &Constraint, + MultiLevelTemplateArgumentList MLTAL, + llvm::SmallVector<TemplateArgument> &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); +} + +} // namespace + +ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint( + const Expr *AtomicExpr, const MultiLevelTemplateArgumentList &MLTAL) { EnterExpressionEvaluationContext ConstantEvaluated( S, Sema::ExpressionEvaluationContext::ConstantEvaluated, Sema::ReuseLambdaContextDecl); + llvm::FoldingSetNodeID ID; + if (Template && + DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, &MLTAL)) { + Satisfaction.IsSatisfied = false; + Satisfaction.ContainsErrors = true; + return ExprEmpty(); + } + SatisfactionStackRAII StackRAII(S, Template, ID); + // Atomic constraint - substitute arguments and check satisfaction. - ExprResult SubstitutedExpression; + ExprResult SubstitutedExpression = const_cast<Expr *>(AtomicExpr); { TemplateDeductionInfo Info(TemplateNameLoc); Sema::InstantiatingTemplate Inst( @@ -220,16 +485,6 @@ static ExprResult EvaluateAtomicConstraint( if (Inst.isInvalid()) return ExprError(); - llvm::FoldingSetNodeID ID; - if (Template && - DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) { - Satisfaction.IsSatisfied = false; - Satisfaction.ContainsErrors = true; - return ExprEmpty(); - } - - SatisfactionStackRAII StackRAII(S, Template, ID); - // We do not want error diagnostics escaping here. Sema::SFINAETrap Trap(S); SubstitutedExpression = @@ -247,21 +502,16 @@ static ExprResult EvaluateAtomicConstraint( PartialDiagnosticAt SubstDiag{SourceLocation(), PartialDiagnostic::NullDiagnostic()}; Info.takeSFINAEDiagnostic(SubstDiag); - // FIXME: Concepts: This is an unfortunate consequence of there + // 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. - 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) ConstraintSatisfaction::SubstitutionDiagnostic{ - SubstDiag.first, StringRef(Mem, MessageSize)}); + new (S.Context) ConstraintSubstitutionDiagnostic{ + SubstDiag.first, + allocateStringFromConceptDiagnostic(S, SubstDiag.second)}); Satisfaction.IsSatisfied = false; return ExprEmpty(); } @@ -289,216 +539,94 @@ static ExprResult EvaluateAtomicConstraint( return SubstitutedExpression; } -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 = FE->getPattern(); +std::optional<MultiLevelTemplateArgumentList> +ConstraintSatisfactionChecker::SubstitutionInTemplateArguments( + const NormalizedConstraintWithParamMapping &Constraint, + MultiLevelTemplateArgumentList MLTAL, + llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) { - SmallVector<UnexpandedParameterPack, 2> Unexpanded; - S.collectUnexpandedParameterPacks(Pattern, Unexpanded); - assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); - bool Expand = true; - bool RetainExpansion = false; - UnsignedOrNone NumExpansions = FE->getNumExpansions(); - if (S.CheckParameterPacksForExpansion( - FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL, - /*FailOnPackProducingTemplates=*/true, Expand, RetainExpansion, - NumExpansions) || - !Expand || RetainExpansion) - return std::nullopt; + if (!Constraint.hasParameterMapping()) + return std::move(MLTAL); - if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) { - S.Diag(FE->getEllipsisLoc(), - clang::diag::err_fold_expression_limit_exceeded) - << *NumExpansions << S.getLangOpts().BracketDepth - << FE->getSourceRange(); - S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth); + TemplateDeductionInfo Info(Constraint.getBeginLoc()); + Sema::InstantiatingTemplate Inst( + S, Constraint.getBeginLoc(), + Sema::InstantiatingTemplate::ConstraintSubstitution{}, + // FIXME: improve const-correctness of InstantiatingTemplate + const_cast<NamedDecl *>(Template), Info, Constraint.getSourceRange()); + if (Inst.isInvalid()) return std::nullopt; - } - return NumExpansions; -} - -static ExprResult calculateConstraintSatisfaction( - Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template, - SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, - ConstraintSatisfaction &Satisfaction); - -static ExprResult calculateConstraintSatisfaction( - Sema &S, const Expr *LHS, OverloadedOperatorKind Op, const Expr *RHS, - const NamedDecl *Template, SourceLocation TemplateNameLoc, - const MultiLevelTemplateArgumentList &MLTAL, - ConstraintSatisfaction &Satisfaction) { - size_t EffectiveDetailEndIndex = Satisfaction.Details.size(); - - ExprResult LHSRes = calculateConstraintSatisfaction( - S, LHS, Template, TemplateNameLoc, MLTAL, Satisfaction); - - if (LHSRes.isInvalid()) - return ExprError(); - - bool IsLHSSatisfied = Satisfaction.IsSatisfied; - - if (Op == clang::OO_PipePipe && IsLHSSatisfied) - // [temp.constr.op] p3 - // A disjunction is a constraint taking two operands. To determine if - // a disjunction is satisfied, the satisfaction of the first operand - // is checked. If that is satisfied, the disjunction is satisfied. - // Otherwise, the disjunction is satisfied if and only if the second - // operand is satisfied. - // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp. - return LHSRes; - - if (Op == clang::OO_AmpAmp && !IsLHSSatisfied) - // [temp.constr.op] p2 - // A conjunction is a constraint taking two operands. To determine if - // a conjunction is satisfied, the satisfaction of the first operand - // is checked. If that is not satisfied, the conjunction is not - // satisfied. Otherwise, the conjunction is satisfied if and only if - // the second operand is satisfied. - // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp. - return LHSRes; - - ExprResult RHSRes = calculateConstraintSatisfaction( - S, RHS, Template, TemplateNameLoc, MLTAL, Satisfaction); - if (RHSRes.isInvalid()) - return ExprError(); - bool IsRHSSatisfied = Satisfaction.IsSatisfied; - // Current implementation adds diagnostic information about the falsity - // of each false atomic constraint expression when it evaluates them. - // When the evaluation results to `false || true`, the information - // generated during the evaluation of left-hand side is meaningless - // because the whole expression evaluates to true. - // The following code removes the irrelevant diagnostic information. - // FIXME: We should probably delay the addition of diagnostic information - // until we know the entire expression is false. - if (Op == clang::OO_PipePipe && IsRHSSatisfied) { - auto EffectiveDetailEnd = Satisfaction.Details.begin(); - std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex); - Satisfaction.Details.erase(EffectiveDetailEnd, Satisfaction.Details.end()); - } - - if (!LHSRes.isUsable() || !RHSRes.isUsable()) - return ExprEmpty(); - - return BinaryOperator::Create(S.Context, LHSRes.get(), RHSRes.get(), - BinaryOperator::getOverloadedOpcode(Op), - S.Context.BoolTy, VK_PRValue, OK_Ordinary, - LHS->getBeginLoc(), FPOptionsOverride{}); -} - -static ExprResult calculateConstraintSatisfaction( - Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template, - SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, - ConstraintSatisfaction &Satisfaction) { - bool Conjunction = FE->getOperator() == BinaryOperatorKind::BO_LAnd; - size_t EffectiveDetailEndIndex = Satisfaction.Details.size(); - - ExprResult Out; - if (FE->isLeftFold() && FE->getInit()) { - Out = calculateConstraintSatisfaction(S, FE->getInit(), Template, - TemplateNameLoc, MLTAL, Satisfaction); - if (Out.isInvalid()) - return ExprError(); + Sema::SFINAETrap Trap(S); - // 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; + 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; } - if (FE->isRightFold() && FE->getInit()) { - ExprResult Res = calculateConstraintSatisfaction( - S, FE->getInit(), Template, TemplateNameLoc, MLTAL, Satisfaction); - if (Out.isInvalid()) - return ExprError(); - - if (Out.isUnset()) - Out = Res; - else if (!Res.isUnset()) { - Out = BinaryOperator::Create( - S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy, - VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{}); + Sema::CheckTemplateArgumentInfo CTAI; + auto *TD = const_cast<TemplateDecl *>( + cast<TemplateDecl>(Constraint.getConstraintDecl())); + if (S.CheckTemplateArgumentList(TD, Constraint.getUsedTemplateParamList(), + TD->getLocation(), SubstArgs, + /*DefaultArguments=*/{}, + /*PartialTemplateArgs=*/false, CTAI)) + return std::nullopt; + const NormalizedConstraint::OccurenceList &Used = + Constraint.mappingOccurenceList(); + SubstitutedOuterMost = + llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost()); + unsigned Offset = 0; + for (unsigned I = 0, MappedIndex = 0; I < Used.size(); I++) { + TemplateArgument Arg; + if (Used[I]) + Arg = S.Context.getCanonicalTemplateArgument( + CTAI.SugaredConverted[MappedIndex++]); + if (I < SubstitutedOuterMost.size()) { + SubstitutedOuterMost[I] = Arg; + Offset = I + 1; + } else { + SubstitutedOuterMost.push_back(Arg); + Offset = SubstitutedOuterMost.size(); } } + if (Offset < SubstitutedOuterMost.size()) + SubstitutedOuterMost.erase(SubstitutedOuterMost.begin() + Offset); - if (Out.isUnset()) { - Satisfaction.IsSatisfied = Conjunction; - Out = S.BuildEmptyCXXFoldExpr(FE->getBeginLoc(), FE->getOperator()); - } - return Out; + MLTAL.replaceOutermostTemplateArguments( + const_cast<NamedDecl *>(Constraint.getConstraintDecl()), + SubstitutedOuterMost); + return std::move(MLTAL); } -static ExprResult calculateConstraintSatisfaction( - Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template, - SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, - ConstraintSatisfaction &Satisfaction) { - ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts(); - - if (LogicalBinOp BO = ConstraintExpr) - return calculateConstraintSatisfaction( - S, BO.getLHS(), BO.getOp(), BO.getRHS(), Template, TemplateNameLoc, - MLTAL, Satisfaction); +ExprResult ConstraintSatisfactionChecker::EvaluateSlow( + const AtomicConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { - if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) { - // These aren't evaluated, so we don't care about cleanups, so we can just - // evaluate these as if the cleanups didn't exist. - return calculateConstraintSatisfaction( - S, C->getSubExpr(), Template, TemplateNameLoc, MLTAL, Satisfaction); - } - - if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr); - FE && S.getLangOpts().CPlusPlus26 && - (FE->getOperator() == BinaryOperatorKind::BO_LAnd || - FE->getOperator() == BinaryOperatorKind::BO_LOr)) { - return calculateConstraintSatisfaction(S, FE, Template, TemplateNameLoc, - MLTAL, Satisfaction); + llvm::SmallVector<TemplateArgument> SubstitutedOuterMost; + std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs = + SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); + if (!SubstitutedArgs) { + Satisfaction.IsSatisfied = false; + return ExprEmpty(); } - // FIXME: We should not treat ConceptSpecializationExpr as atomic constraints. - - // An atomic constraint expression + Sema::ArgPackSubstIndexRAII SubstIndex(S, PackSubstitutionIndex); ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint( - S, ConstraintExpr, Template, TemplateNameLoc, MLTAL, Satisfaction); + Constraint.getConstraintExpr(), *SubstitutedArgs); if (SubstitutedAtomicExpr.isInvalid()) return ExprError(); - if (!SubstitutedAtomicExpr.isUsable()) + if (SubstitutedAtomicExpr.isUnset()) // Evaluator has decided satisfaction without yielding an expression. return ExprEmpty(); @@ -512,16 +640,16 @@ static ExprResult calculateConstraintSatisfaction( Satisfaction.ContainsErrors = true; PartialDiagnostic Msg = S.PDiag(diag::note_constraint_references_error); - SmallString<128> DiagString; - DiagString = ": "; - Msg.EmitToString(S.getDiagnostics(), DiagString); - unsigned MessageSize = DiagString.size(); - char *Mem = new (S.Context) char[MessageSize]; - memcpy(Mem, DiagString.c_str(), MessageSize); Satisfaction.Details.emplace_back( - new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{ + new (S.Context) ConstraintSubstitutionDiagnostic{ SubstitutedAtomicExpr.get()->getBeginLoc(), - StringRef(Mem, MessageSize)}); + allocateStringFromConceptDiagnostic(S, Msg)}); + return SubstitutedAtomicExpr; + } + + if (SubstitutedAtomicExpr.get()->isValueDependent()) { + Satisfaction.IsSatisfied = true; + Satisfaction.ContainsErrors = false; return SubstitutedAtomicExpr; } @@ -552,21 +680,384 @@ static ExprResult calculateConstraintSatisfaction( return SubstitutedAtomicExpr; } -static ExprResult calculateConstraintSatisfaction( - Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc, - const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, - ConstraintSatisfaction &Satisfaction) { +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) { + + // We should ignore errors in the presence of packs of different size. + Sema::SFINAETrap Trap(S); + + Expr *Pattern = const_cast<Expr *>(FE.getPattern()); + + SmallVector<UnexpandedParameterPack, 2> Unexpanded; + S.collectUnexpandedParameterPacks(Pattern, Unexpanded); + assert(!Unexpanded.empty() && "Pack expansion without parameter packs?"); + bool Expand = true; + bool RetainExpansion = false; + UnsignedOrNone NumExpansions(std::nullopt); + if (S.CheckParameterPacksForExpansion( + Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL, + /*FailOnPackProducingTemplates=*/false, Expand, RetainExpansion, + NumExpansions) || + !Expand || RetainExpansion) + return std::nullopt; + + if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) { + S.Diag(Pattern->getExprLoc(), + clang::diag::err_fold_expression_limit_exceeded) + << *NumExpansions << S.getLangOpts().BracketDepth + << Pattern->getSourceRange(); + S.Diag(Pattern->getExprLoc(), diag::note_bracket_depth); + return std::nullopt; + } + return NumExpansions; +} - return calculateConstraintSatisfaction(S, ConstraintExpr, Template, - TemplateNameLoc, MLTAL, Satisfaction); +ExprResult ConstraintSatisfactionChecker::EvaluateSlow( + const FoldExpandedConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { + + bool Conjunction = Constraint.getFoldOperator() == + FoldExpandedConstraint::FoldOperatorKind::And; + unsigned EffectiveDetailEndIndex = Satisfaction.Details.size(); + + llvm::SmallVector<TemplateArgument> SubstitutedOuterMost; + // FIXME: Is PackSubstitutionIndex correct? + llvm::SaveAndRestore _(PackSubstitutionIndex, S.ArgPackSubstIndex); + std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs = + SubstitutionInTemplateArguments( + static_cast<const NormalizedConstraintWithParamMapping &>(Constraint), + MLTAL, SubstitutedOuterMost); + if (!SubstitutedArgs) { + Satisfaction.IsSatisfied = false; + return ExprError(); + } + + ExprResult Out; + UnsignedOrNone NumExpansions = + EvaluateFoldExpandedConstraintSize(Constraint, *SubstitutedArgs); + if (!NumExpansions) + return ExprEmpty(); + + if (*NumExpansions == 0) { + Satisfaction.IsSatisfied = Conjunction; + return ExprEmpty(); + } + + 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; + } + + return Out; +} + +ExprResult ConstraintSatisfactionChecker::Evaluate( + const FoldExpandedConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { + + 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; +} + +ExprResult ConstraintSatisfactionChecker::EvaluateSlow( + const ConceptIdConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL, unsigned Size) { + const ConceptReference *ConceptId = Constraint.getConceptId(); + + llvm::SmallVector<TemplateArgument> SubstitutedOuterMost; + std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs = + SubstitutionInTemplateArguments(Constraint, MLTAL, SubstitutedOuterMost); + + if (!SubstitutedArgs) { + Satisfaction.IsSatisfied = false; + // FIXME: diagnostics? + return ExprError(); + } + + 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<NamedDecl *>(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()) + 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(); + } + + 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<ConceptSpecializationExpr>() + ->getConceptReference())); + } + return SubstitutedConceptId; +} + +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(); + + ExprResult E = Evaluate(Constraint.getNormalizedConstraint(), MLTAL); + + if (!E.isUsable()) { + Satisfaction.Details.insert(Satisfaction.Details.begin() + Size, ConceptId); + return E; + } + + // 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; + + ExprResult LHS = Evaluate(Constraint.getLHS(), MLTAL); + + if (Conjunction && (!Satisfaction.IsSatisfied || Satisfaction.ContainsErrors)) + return LHS; + + if (!Conjunction && LHS.isUsable() && Satisfaction.IsSatisfied && + !Satisfaction.ContainsErrors) + return LHS; + + Satisfaction.ContainsErrors = false; + Satisfaction.IsSatisfied = false; + + ExprResult RHS = Evaluate(Constraint.getRHS(), MLTAL); + + if (RHS.isUsable() && Satisfaction.IsSatisfied && + !Satisfaction.ContainsErrors) + Satisfaction.Details.erase(Satisfaction.Details.begin() + + EffectiveDetailEndIndex, + Satisfaction.Details.end()); + + if (!LHS.isUsable()) + return RHS; + + if (!RHS.isUsable()) + return LHS; + + return BinaryOperator::Create(S.Context, LHS.get(), RHS.get(), + Conjunction ? BinaryOperatorKind::BO_LAnd + : BinaryOperatorKind::BO_LOr, + S.Context.BoolTy, VK_PRValue, OK_Ordinary, + Constraint.getBeginLoc(), FPOptionsOverride{}); +} + +ExprResult ConstraintSatisfactionChecker::Evaluate( + const NormalizedConstraint &Constraint, + const MultiLevelTemplateArgumentList &MLTAL) { + switch (Constraint.getKind()) { + case NormalizedConstraint::ConstraintKind::Atomic: + return Evaluate(static_cast<const AtomicConstraint &>(Constraint), MLTAL); + + case NormalizedConstraint::ConstraintKind::FoldExpanded: + return Evaluate(static_cast<const FoldExpandedConstraint &>(Constraint), + MLTAL); + + case NormalizedConstraint::ConstraintKind::ConceptId: + return Evaluate(static_cast<const ConceptIdConstraint &>(Constraint), + MLTAL); + + case NormalizedConstraint::ConstraintKind::Compound: + return Evaluate(static_cast<const CompoundConstraint &>(Constraint), MLTAL); + } } static bool CheckConstraintSatisfaction( Sema &S, const NamedDecl *Template, ArrayRef<AssociatedConstraint> AssociatedConstraints, - llvm::SmallVectorImpl<Expr *> &Converted, const MultiLevelTemplateArgumentList &TemplateArgsLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction, + Expr **ConvertedExpr, const ConceptReference *TopLevelConceptId = nullptr) { + + if (ConvertedExpr) + *ConvertedExpr = nullptr; + if (AssociatedConstraints.empty()) { Satisfaction.IsSatisfied = true; return false; @@ -578,57 +1069,60 @@ static bool CheckConstraintSatisfaction( return false; } - ArrayRef<TemplateArgument> TemplateArgs = - TemplateArgsLists.getNumSubstitutedLevels() > 0 - ? TemplateArgsLists.getOutermost() - : ArrayRef<TemplateArgument>{}; - Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), - Sema::InstantiatingTemplate::ConstraintsCheck{}, - const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange); - if (Inst.isInvalid()) + llvm::ArrayRef<TemplateArgument> Args; + if (TemplateArgsLists.getNumLevels() != 0) + Args = TemplateArgsLists.getInnermost(); + + std::optional<Sema::InstantiatingTemplate> SynthesisContext; + if (!TopLevelConceptId) { + SynthesisContext.emplace(S, TemplateIDRange.getBegin(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, + const_cast<NamedDecl *>(Template), Args, + TemplateIDRange); + } + + const NormalizedConstraint *C = + S.getNormalizedAssociatedConstraints(Template, AssociatedConstraints); + if (!C) { + Satisfaction.IsSatisfied = false; return true; + } - for (const AssociatedConstraint &AC : AssociatedConstraints) { - if (AC.isNull()) - return true; + if (TopLevelConceptId) + C = ConceptIdConstraint::Create(S.getASTContext(), TopLevelConceptId, + const_cast<NormalizedConstraint *>(C), + Template, /*CSE=*/nullptr, + S.ArgPackSubstIndex); - Sema::ArgPackSubstIndexRAII _(S, AC.ArgPackSubstIndex); - ExprResult Res = calculateConstraintSatisfaction( - S, Template, TemplateIDRange.getBegin(), TemplateArgsLists, - AC.ConstraintExpr, Satisfaction); - if (Res.isInvalid()) - return true; + ExprResult Res = + ConstraintSatisfactionChecker(S, Template, TemplateIDRange.getBegin(), + S.ArgPackSubstIndex, Satisfaction) + .Evaluate(*C, TemplateArgsLists); + + if (Res.isInvalid()) + return true; + + if (Res.isUsable() && ConvertedExpr) + *ConvertedExpr = Res.get(); - Converted.push_back(Res.get()); - if (!Satisfaction.IsSatisfied) { - // Backfill the 'converted' list with nulls so we can keep the Converted - // and unconverted lists in sync. - Converted.append(AssociatedConstraints.size() - Converted.size(), - nullptr); - // [temp.constr.op] p2 - // [...] To determine if a conjunction is satisfied, the satisfaction - // of the first operand is checked. If that is not satisfied, the - // conjunction is not satisfied. [...] - return false; - } - } return false; } bool Sema::CheckConstraintSatisfaction( - const NamedDecl *Template, + ConstrainedDeclOrNestedRequirement Entity, ArrayRef<AssociatedConstraint> AssociatedConstraints, - llvm::SmallVectorImpl<Expr *> &ConvertedConstraints, const MultiLevelTemplateArgumentList &TemplateArgsLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { + SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction, + const ConceptReference *TopLevelConceptId, Expr **ConvertedExpr) { if (AssociatedConstraints.empty()) { OutSatisfaction.IsSatisfied = true; return false; } + const auto *Template = Entity.dyn_cast<const NamedDecl *>(); if (!Template) { return ::CheckConstraintSatisfaction( - *this, nullptr, AssociatedConstraints, ConvertedConstraints, - TemplateArgsLists, TemplateIDRange, OutSatisfaction); + *this, nullptr, AssociatedConstraints, TemplateArgsLists, + TemplateIDRange, OutSatisfaction, ConvertedExpr, TopLevelConceptId); } // Invalid templates could make their way here. Substituting them could result // in dependent expressions. @@ -643,10 +1137,15 @@ bool Sema::CheckConstraintSatisfaction( // here. llvm::SmallVector<TemplateArgument, 4> FlattenedArgs; for (auto List : TemplateArgsLists) - llvm::append_range(FlattenedArgs, List.Args); + for (const TemplateArgument &Arg : List.Args) + FlattenedArgs.emplace_back(Context.getCanonicalTemplateArgument(Arg)); + + const NamedDecl *Owner = Template; + if (TopLevelConceptId) + Owner = TopLevelConceptId->getNamedConcept(); llvm::FoldingSetNodeID ID; - ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs); + ConstraintSatisfaction::Profile(ID, Context, Owner, FlattenedArgs); void *InsertPos; if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) { OutSatisfaction = *Cached; @@ -654,11 +1153,11 @@ bool Sema::CheckConstraintSatisfaction( } auto Satisfaction = - std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs); - if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints, - ConvertedConstraints, TemplateArgsLists, - TemplateIDRange, *Satisfaction)) { - OutSatisfaction = *Satisfaction; + std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs); + if (::CheckConstraintSatisfaction( + *this, Template, AssociatedConstraints, TemplateArgsLists, + TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) { + OutSatisfaction = std::move(*Satisfaction); return true; } @@ -688,14 +1187,18 @@ bool Sema::CheckConstraintSatisfaction( const ConceptSpecializationExpr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { + llvm::SmallVector<AssociatedConstraint, 1> Constraints; + Constraints.emplace_back( + ConstraintExpr->getNamedConcept()->getConstraintExpr()); + MultiLevelTemplateArgumentList MLTAL(ConstraintExpr->getNamedConcept(), ConstraintExpr->getTemplateArguments(), true); - return calculateConstraintSatisfaction( - *this, ConstraintExpr, ConstraintExpr->getNamedConcept(), - ConstraintExpr->getConceptNameLoc(), MLTAL, Satisfaction) - .isInvalid(); + return CheckConstraintSatisfaction( + ConstraintExpr->getNamedConcept(), Constraints, MLTAL, + ConstraintExpr->getSourceRange(), Satisfaction, + ConstraintExpr->getConceptReference()); } bool Sema::SetupConstraintScope( @@ -854,50 +1357,6 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, Satisfaction); } - -// Figure out the to-translation-unit depth for this function declaration for -// the purpose of seeing if they differ by constraints. This isn't the same as -// getTemplateDepth, because it includes already instantiated parents. -static unsigned -CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND, - bool SkipForSpecialization = false) { - MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - ND, ND->getLexicalDeclContext(), /*Final=*/false, - /*Innermost=*/std::nullopt, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true, SkipForSpecialization); - return MLTAL.getNumLevels(); -} - -namespace { - class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> { - unsigned TemplateDepth = 0; - public: - using inherited = TreeTransform<AdjustConstraintDepth>; - AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth) - : inherited(SemaRef), TemplateDepth(TemplateDepth) {} - - using inherited::TransformTemplateTypeParmType; - QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB, - TemplateTypeParmTypeLoc TL, bool) { - const TemplateTypeParmType *T = TL.getTypePtr(); - - TemplateTypeParmDecl *NewTTPDecl = nullptr; - if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl()) - NewTTPDecl = cast_or_null<TemplateTypeParmDecl>( - TransformDecl(TL.getNameLoc(), OldTTPDecl)); - - QualType Result = getSema().Context.getTemplateTypeParmType( - T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(), - NewTTPDecl); - TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result); - NewTL.setNameLoc(TL.getNameLoc()); - return Result; - } - }; -} // namespace - static const Expr *SubstituteConstraintExpressionWithoutSatisfaction( Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo, const Expr *ConstrExpr) { @@ -1161,73 +1620,61 @@ bool Sema::CheckFunctionTemplateConstraints( static void diagnoseUnsatisfiedRequirement(Sema &S, concepts::ExprRequirement *Req, bool First) { - assert(!Req->isSatisfied() - && "Diagnose() can only be used on an unsatisfied requirement"); + assert(!Req->isSatisfied() && + "Diagnose() can only be used on an unsatisfied requirement"); switch (Req->getSatisfactionStatus()) { - case concepts::ExprRequirement::SS_Dependent: - llvm_unreachable("Diagnosing a dependent requirement"); - break; - case concepts::ExprRequirement::SS_ExprSubstitutionFailure: { - auto *SubstDiag = Req->getExprSubstitutionDiagnostic(); - if (!SubstDiag->DiagMessage.empty()) - S.Diag(SubstDiag->DiagLoc, - diag::note_expr_requirement_expr_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity - << SubstDiag->DiagMessage; - else - S.Diag(SubstDiag->DiagLoc, - diag::note_expr_requirement_expr_unknown_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity; - break; - } - case concepts::ExprRequirement::SS_NoexceptNotMet: - S.Diag(Req->getNoexceptLoc(), - diag::note_expr_requirement_noexcept_not_met) - << (int)First << Req->getExpr(); - break; - case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: { - auto *SubstDiag = - Req->getReturnTypeRequirement().getSubstitutionDiagnostic(); - if (!SubstDiag->DiagMessage.empty()) - S.Diag(SubstDiag->DiagLoc, - diag::note_expr_requirement_type_requirement_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity - << SubstDiag->DiagMessage; - else - S.Diag(SubstDiag->DiagLoc, - diag::note_expr_requirement_type_requirement_unknown_substitution_error) - << (int)First << SubstDiag->SubstitutedEntity; - break; - } - case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: { - ConceptSpecializationExpr *ConstraintExpr = - Req->getReturnTypeRequirementSubstitutedConstraintExpr(); - if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) { - // A simple case - expr type is the type being constrained and the concept - // was not provided arguments. - Expr *e = Req->getExpr(); - S.Diag(e->getBeginLoc(), - diag::note_expr_requirement_constraints_not_satisfied_simple) - << (int)First << S.Context.getReferenceQualifiedType(e) - << ConstraintExpr->getNamedConcept(); - } else { - S.Diag(ConstraintExpr->getBeginLoc(), - diag::note_expr_requirement_constraints_not_satisfied) - << (int)First << ConstraintExpr; - } - S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction()); - break; - } - case concepts::ExprRequirement::SS_Satisfied: - llvm_unreachable("We checked this above"); + case concepts::ExprRequirement::SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + break; + case concepts::ExprRequirement::SS_ExprSubstitutionFailure: { + auto *SubstDiag = Req->getExprSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_NoexceptNotMet: + S.Diag(Req->getNoexceptLoc(), diag::note_expr_requirement_noexcept_not_met) + << (int)First << Req->getExpr(); + break; + case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: { + auto *SubstDiag = + Req->getReturnTypeRequirement().getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag( + SubstDiag->DiagLoc, + diag:: + note_expr_requirement_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: { + ConceptSpecializationExpr *ConstraintExpr = + Req->getReturnTypeRequirementSubstitutedConstraintExpr(); + S.DiagnoseUnsatisfiedConstraint(ConstraintExpr); + break; + } + case concepts::ExprRequirement::SS_Satisfied: + llvm_unreachable("We checked this above"); } } static void diagnoseUnsatisfiedRequirement(Sema &S, concepts::TypeRequirement *Req, bool First) { - assert(!Req->isSatisfied() - && "Diagnose() can only be used on an unsatisfied requirement"); + assert(!Req->isSatisfied() && + "Diagnose() can only be used on an unsatisfied requirement"); switch (Req->getSatisfactionStatus()) { case concepts::TypeRequirement::SS_Dependent: llvm_unreachable("Diagnosing a dependent requirement"); @@ -1235,9 +1682,9 @@ static void diagnoseUnsatisfiedRequirement(Sema &S, case concepts::TypeRequirement::SS_SubstitutionFailure: { auto *SubstDiag = Req->getSubstitutionDiagnostic(); if (!SubstDiag->DiagMessage.empty()) - S.Diag(SubstDiag->DiagLoc, - diag::note_type_requirement_substitution_error) << (int)First - << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage; + S.Diag(SubstDiag->DiagLoc, diag::note_type_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; else S.Diag(SubstDiag->DiagLoc, diag::note_type_requirement_unknown_substitution_error) @@ -1249,31 +1696,53 @@ static void diagnoseUnsatisfiedRequirement(Sema &S, return; } } -static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, - Expr *SubstExpr, - bool First = true); + +static void diagnoseUnsatisfiedConceptIdExpr(Sema &S, + const ConceptReference *Concept, + SourceLocation Loc, bool First) { + if (Concept->getTemplateArgsAsWritten()->NumTemplateArgs == 1) { + S.Diag( + Loc, + diag:: + note_single_arg_concept_specialization_constraint_evaluated_to_false) + << (int)First + << Concept->getTemplateArgsAsWritten()->arguments()[0].getArgument() + << Concept->getNamedConcept(); + } else { + S.Diag(Loc, diag::note_concept_specialization_constraint_evaluated_to_false) + << (int)First << Concept; + } +} + +static void diagnoseUnsatisfiedConstraintExpr( + Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc, + bool First, concepts::NestedRequirement *Req = nullptr); + +static void DiagnoseUnsatisfiedConstraint( + Sema &S, ArrayRef<UnsatisfiedConstraintRecord> Records, SourceLocation Loc, + bool First = true, concepts::NestedRequirement *Req = nullptr) { + for (auto &Record : Records) { + diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req); + Loc = {}; + First = isa<const ConceptReference *>(Record); + } +} static void diagnoseUnsatisfiedRequirement(Sema &S, concepts::NestedRequirement *Req, bool First) { - using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>; - for (auto &Record : Req->getConstraintSatisfaction()) { - if (auto *SubstDiag = Record.dyn_cast<SubstitutionDiagnostic *>()) - S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error) - << (int)First << Req->getInvalidConstraintEntity() - << SubstDiag->second; - else - diagnoseWellFormedUnsatisfiedConstraintExpr(S, Record.dyn_cast<Expr *>(), - First); - First = false; - } + DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(), + Req->hasInvalidConstraint() + ? SourceLocation() + : Req->getConstraintExpr()->getExprLoc(), + First, Req); } static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, - Expr *SubstExpr, + const Expr *SubstExpr, bool First) { SubstExpr = SubstExpr->IgnoreParenImpCasts(); - if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) { + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) { switch (BO->getOpcode()) { // These two cases will in practice only be reached when using fold // expressions with || and &&, since otherwise the || and && will have been @@ -1319,7 +1788,7 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, BO->getRHS()->EvaluateAsInt(SimplifiedRHS, S.Context, Expr::SE_NoSideEffects, /*InConstantContext=*/true); - if (!SimplifiedLHS.Diag && ! SimplifiedRHS.Diag) { + if (!SimplifiedLHS.Diag && !SimplifiedRHS.Diag) { S.Diag(SubstExpr->getBeginLoc(), diag::note_atomic_constraint_evaluated_to_false_elaborated) << (int)First << SubstExpr @@ -1334,22 +1803,6 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, default: break; } - } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) { - if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) { - S.Diag( - CSE->getSourceRange().getBegin(), - diag:: - note_single_arg_concept_specialization_constraint_evaluated_to_false) - << (int)First - << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument() - << CSE->getNamedConcept(); - } else { - S.Diag(SubstExpr->getSourceRange().getBegin(), - diag::note_concept_specialization_constraint_evaluated_to_false) - << (int)First << CSE; - } - S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); - return; } else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) { // FIXME: RequiresExpr should store dependent diagnostics. for (concepts::Requirement *Req : RE->getRequirements()) @@ -1364,6 +1817,10 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, break; } return; + } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) { + // Drill down concept ids treated as atomic constraints + S.DiagnoseUnsatisfiedConstraint(CSE, First); + return; } else if (auto *TTE = dyn_cast<TypeTraitExpr>(SubstExpr); TTE && TTE->getTrait() == clang::TypeTrait::BTT_IsDeducible) { assert(TTE->getNumArgs() == 2); @@ -1379,216 +1836,332 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, S.DiagnoseTypeTraitDetails(SubstExpr); } -template <typename SubstitutionDiagnostic> static void diagnoseUnsatisfiedConstraintExpr( - Sema &S, const llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> &Record, - bool First = true) { - if (auto *Diag = Record.template dyn_cast<SubstitutionDiagnostic *>()) { - S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed) - << Diag->second; + Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc, + bool First, concepts::NestedRequirement *Req) { + if (auto *Diag = + Record + .template dyn_cast<const ConstraintSubstitutionDiagnostic *>()) { + if (Req) + S.Diag(Diag->first, diag::note_nested_requirement_substitution_error) + << (int)First << Req->getInvalidConstraintEntity() << Diag->second; + else + S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed) + << Diag->second; return; } - - diagnoseWellFormedUnsatisfiedConstraintExpr(S, cast<Expr *>(Record), First); + if (const auto *Concept = dyn_cast<const ConceptReference *>(Record)) { + if (Loc.isInvalid()) + Loc = Concept->getBeginLoc(); + diagnoseUnsatisfiedConceptIdExpr(S, Concept, Loc, First); + return; + } + diagnoseWellFormedUnsatisfiedConstraintExpr( + S, cast<const class Expr *>(Record), First); } -void -Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction, - bool First) { +void Sema::DiagnoseUnsatisfiedConstraint( + const ConstraintSatisfaction &Satisfaction, SourceLocation Loc, + bool First) { + assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - for (auto &Record : Satisfaction.Details) { - diagnoseUnsatisfiedConstraintExpr(*this, Record, First); - First = false; - } + ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.Details, Loc, First); } void Sema::DiagnoseUnsatisfiedConstraint( - const ASTConstraintSatisfaction &Satisfaction, - bool First) { + const ConceptSpecializationExpr *ConstraintExpr, bool First) { + + const ASTConstraintSatisfaction &Satisfaction = + ConstraintExpr->getSatisfaction(); + assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - for (auto &Record : Satisfaction) { - diagnoseUnsatisfiedConstraintExpr(*this, Record, First); - First = false; - } + + ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(), + ConstraintExpr->getBeginLoc(), First); } -const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints( - const NamedDecl *ConstrainedDecl, - ArrayRef<AssociatedConstraint> AssociatedConstraints) { - // In case the ConstrainedDecl comes from modules, it is necessary to use - // the canonical decl to avoid different atomic constraints with the 'same' - // declarations. - ConstrainedDecl = cast<NamedDecl>(ConstrainedDecl->getCanonicalDecl()); +namespace { - auto CacheEntry = NormalizationCache.find(ConstrainedDecl); - if (CacheEntry == NormalizationCache.end()) { - auto Normalized = NormalizedConstraint::fromAssociatedConstraints( - *this, ConstrainedDecl, AssociatedConstraints); - CacheEntry = - NormalizationCache - .try_emplace(ConstrainedDecl, - Normalized - ? new (Context) NormalizedConstraint( - std::move(*Normalized)) - : nullptr) - .first; - } - return CacheEntry->second; -} +class SubstituteParameterMappings { + Sema &SemaRef; -const NormalizedConstraint *clang::getNormalizedAssociatedConstraints( - Sema &S, const NamedDecl *ConstrainedDecl, - ArrayRef<AssociatedConstraint> AssociatedConstraints) { - return S.getNormalizedAssociatedConstraints(ConstrainedDecl, - AssociatedConstraints); -} + const MultiLevelTemplateArgumentList *MLTAL; + const ASTTemplateArgumentListInfo *ArgsAsWritten; -static bool -substituteParameterMappings(Sema &S, NormalizedConstraint &N, - ConceptDecl *Concept, - const MultiLevelTemplateArgumentList &MLTAL, - const ASTTemplateArgumentListInfo *ArgsAsWritten) { + bool InFoldExpr; - if (N.isCompound()) { - if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL, - ArgsAsWritten)) - return true; - return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL, - ArgsAsWritten); - } + SubstituteParameterMappings(Sema &SemaRef, + const MultiLevelTemplateArgumentList *MLTAL, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + bool InFoldExpr) + : SemaRef(SemaRef), MLTAL(MLTAL), ArgsAsWritten(ArgsAsWritten), + InFoldExpr(InFoldExpr) {} + + void buildParameterMapping(NormalizedConstraintWithParamMapping &N); + + bool substitute(NormalizedConstraintWithParamMapping &N); + + bool substitute(ConceptIdConstraint &CC); + +public: + SubstituteParameterMappings(Sema &SemaRef, bool InFoldExpr = false) + : SemaRef(SemaRef), MLTAL(nullptr), ArgsAsWritten(nullptr), + InFoldExpr(InFoldExpr) {} + + bool substitute(NormalizedConstraint &N); +}; - if (N.isFoldExpanded()) { - Sema::ArgPackSubstIndexRAII _(S, std::nullopt); - return substituteParameterMappings( - S, N.getFoldExpandedConstraint()->Constraint, Concept, MLTAL, - ArgsAsWritten); +void SubstituteParameterMappings::buildParameterMapping( + NormalizedConstraintWithParamMapping &N) { + TemplateParameterList *TemplateParams = + cast<TemplateDecl>(N.getConstraintDecl())->getTemplateParameters(); + + llvm::SmallBitVector OccurringIndices(TemplateParams->size()); + llvm::SmallBitVector OccurringIndicesForSubsumption(TemplateParams->size()); + + if (N.getKind() == NormalizedConstraint::ConstraintKind::Atomic) { + SemaRef.MarkUsedTemplateParameters( + static_cast<AtomicConstraint &>(N).getConstraintExpr(), + /*OnlyDeduced=*/false, + /*Depth=*/0, OccurringIndices); + + SemaRef.MarkUsedTemplateParametersForSubsumptionParameterMapping( + static_cast<AtomicConstraint &>(N).getConstraintExpr(), + /*Depth=*/0, OccurringIndicesForSubsumption); + + } else if (N.getKind() == + NormalizedConstraint::ConstraintKind::FoldExpanded) { + SemaRef.MarkUsedTemplateParameters( + static_cast<FoldExpandedConstraint &>(N).getPattern(), + /*OnlyDeduced=*/false, + /*Depth=*/0, OccurringIndices); + } else if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) { + auto *Args = static_cast<ConceptIdConstraint &>(N) + .getConceptId() + ->getTemplateArgsAsWritten(); + if (Args) + SemaRef.MarkUsedTemplateParameters(Args->arguments(), + /*Depth=*/0, OccurringIndices); } + TemplateArgumentLoc *TempArgs = + new (SemaRef.Context) TemplateArgumentLoc[OccurringIndices.count()]; + llvm::SmallVector<NamedDecl *> UsedParams; + for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) { + SourceLocation Loc = ArgsAsWritten->NumTemplateArgs > I + ? ArgsAsWritten->arguments()[I].getLocation() + : SourceLocation(); + // FIXME: Investigate why we couldn't always preserve the SourceLoc. We + // can't assert Loc.isValid() now. + if (OccurringIndices[I]) { + NamedDecl *Param = TemplateParams->begin()[I]; + new (&(TempArgs)[J]) TemplateArgumentLoc( + SemaRef.getIdentityTemplateArgumentLoc(Param, Loc)); + UsedParams.push_back(Param); + J++; + } + } + auto *UsedList = TemplateParameterList::Create( + SemaRef.Context, TemplateParams->getTemplateLoc(), + TemplateParams->getLAngleLoc(), UsedParams, + /*RAngleLoc=*/SourceLocation(), + /*RequiresClause=*/nullptr); + unsigned Size = OccurringIndices.count(); + N.updateParameterMapping( + std::move(OccurringIndices), std::move(OccurringIndicesForSubsumption), + MutableArrayRef<TemplateArgumentLoc>{TempArgs, Size}, UsedList); +} - TemplateParameterList *TemplateParams = Concept->getTemplateParameters(); +bool SubstituteParameterMappings::substitute( + NormalizedConstraintWithParamMapping &N) { + if (!N.hasParameterMapping()) + buildParameterMapping(N); - AtomicConstraint &Atomic = *N.getAtomicConstraint(); - TemplateArgumentListInfo SubstArgs; - if (!Atomic.ParameterMapping) { - llvm::SmallBitVector OccurringIndices(TemplateParams->size()); - S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false, - /*Depth=*/0, OccurringIndices); - TemplateArgumentLoc *TempArgs = - new (S.Context) TemplateArgumentLoc[OccurringIndices.count()]; - for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I) - if (OccurringIndices[I]) - new (&(TempArgs)[J++]) - TemplateArgumentLoc(S.getIdentityTemplateArgumentLoc( - TemplateParams->begin()[I], - // Here we assume we do not support things like - // template<typename A, typename B> - // concept C = ...; - // - // template<typename... Ts> requires C<Ts...> - // struct S { }; - // The above currently yields a diagnostic. - // We still might have default arguments for concept parameters. - ArgsAsWritten->NumTemplateArgs > I - ? ArgsAsWritten->arguments()[I].getLocation() - : SourceLocation())); - Atomic.ParameterMapping.emplace(TempArgs, OccurringIndices.count()); - } - SourceLocation InstLocBegin = - ArgsAsWritten->arguments().empty() - ? ArgsAsWritten->getLAngleLoc() - : ArgsAsWritten->arguments().front().getSourceRange().getBegin(); - SourceLocation InstLocEnd = - ArgsAsWritten->arguments().empty() - ? ArgsAsWritten->getRAngleLoc() - : ArgsAsWritten->arguments().front().getSourceRange().getEnd(); + SourceLocation InstLocBegin, InstLocEnd; + llvm::ArrayRef Arguments = ArgsAsWritten->arguments(); + if (Arguments.empty()) { + InstLocBegin = ArgsAsWritten->getLAngleLoc(); + InstLocEnd = ArgsAsWritten->getRAngleLoc(); + } else { + auto SR = Arguments[0].getSourceRange(); + InstLocBegin = SR.getBegin(); + InstLocEnd = SR.getEnd(); + } Sema::InstantiatingTemplate Inst( - S, InstLocBegin, + SemaRef, InstLocBegin, Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, - const_cast<NamedDecl *>(Atomic.ConstraintDecl), + const_cast<NamedDecl *>(N.getConstraintDecl()), {InstLocBegin, InstLocEnd}); if (Inst.isInvalid()) return true; - if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs)) + + // TransformTemplateArguments is unable to preserve the source location of a + // pack. The SourceLocation is necessary for the instantiation location. + // FIXME: The BaseLoc will be used as the location of the pack expansion, + // which is wrong. + TemplateArgumentListInfo SubstArgs; + if (SemaRef.SubstTemplateArgumentsInParameterMapping( + N.getParameterMapping(), N.getBeginLoc(), *MLTAL, SubstArgs, + /*BuildPackExpansionTypes=*/!InFoldExpr)) + return true; + Sema::CheckTemplateArgumentInfo CTAI; + auto *TD = + const_cast<TemplateDecl *>(cast<TemplateDecl>(N.getConstraintDecl())); + if (SemaRef.CheckTemplateArgumentList(TD, N.getUsedTemplateParamList(), + TD->getLocation(), SubstArgs, + /*DefaultArguments=*/{}, + /*PartialTemplateArgs=*/false, CTAI)) return true; TemplateArgumentLoc *TempArgs = - new (S.Context) TemplateArgumentLoc[SubstArgs.size()]; - std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(), - TempArgs); - Atomic.ParameterMapping.emplace(TempArgs, SubstArgs.size()); + new (SemaRef.Context) TemplateArgumentLoc[CTAI.SugaredConverted.size()]; + + for (unsigned I = 0; I < CTAI.SugaredConverted.size(); ++I) { + SourceLocation Loc; + // If this is an empty pack, we have no corresponding SubstArgs. + if (I < SubstArgs.size()) + Loc = SubstArgs.arguments()[I].getLocation(); + + TempArgs[I] = SemaRef.getTrivialTemplateArgumentLoc( + CTAI.SugaredConverted[I], QualType(), Loc); + } + + MutableArrayRef<TemplateArgumentLoc> Mapping(TempArgs, + CTAI.SugaredConverted.size()); + N.updateParameterMapping(N.mappingOccurenceList(), + N.mappingOccurenceListForSubsumption(), Mapping, + N.getUsedTemplateParamList()); return false; } -static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, - const ConceptSpecializationExpr *CSE) { - MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( - CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(), - /*Final=*/false, CSE->getTemplateArguments(), - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true); +bool SubstituteParameterMappings::substitute(ConceptIdConstraint &CC) { + assert(CC.getConstraintDecl() && MLTAL && ArgsAsWritten); - return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL, - CSE->getTemplateArgsAsWritten()); -} + if (substitute(static_cast<NormalizedConstraintWithParamMapping &>(CC))) + return true; -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); + auto *CSE = CC.getConceptSpecializationExpr(); + assert(CSE); + assert(!CC.getBeginLoc().isInvalid()); + + SourceLocation InstLocBegin, InstLocEnd; + if (llvm::ArrayRef Arguments = ArgsAsWritten->arguments(); + Arguments.empty()) { + InstLocBegin = ArgsAsWritten->getLAngleLoc(); + InstLocEnd = ArgsAsWritten->getRAngleLoc(); } else { - Constraint = CompoundConstraint( - new (C) - NormalizedConstraintPair{NormalizedConstraint(C, Other.getLHS()), - NormalizedConstraint(C, Other.getRHS())}, - Other.getCompoundKind()); + auto SR = Arguments[0].getSourceRange(); + InstLocBegin = SR.getBegin(); + InstLocEnd = SR.getEnd(); } -} + // This is useful for name lookup across modules; see Sema::getLookupModules. + Sema::InstantiatingTemplate Inst( + SemaRef, InstLocBegin, + Sema::InstantiatingTemplate::ParameterMappingSubstitution{}, + const_cast<NamedDecl *>(CC.getConstraintDecl()), + {InstLocBegin, InstLocEnd}); + if (Inst.isInvalid()) + return true; -NormalizedConstraint &NormalizedConstraint::getLHS() const { - assert(isCompound() && "getLHS called on a non-compound constraint."); - return cast<CompoundConstraint>(Constraint).getPointer()->LHS; + TemplateArgumentListInfo Out; + // TransformTemplateArguments is unable to preserve the source location of a + // pack. The SourceLocation is necessary for the instantiation location. + // FIXME: The BaseLoc will be used as the location of the pack expansion, + // which is wrong. + const ASTTemplateArgumentListInfo *ArgsAsWritten = + CSE->getTemplateArgsAsWritten(); + if (SemaRef.SubstTemplateArgumentsInParameterMapping( + ArgsAsWritten->arguments(), CC.getBeginLoc(), *MLTAL, Out, + /*BuildPackExpansionTypes=*/!InFoldExpr)) + return true; + Sema::CheckTemplateArgumentInfo CTAI; + if (SemaRef.CheckTemplateArgumentList(CSE->getNamedConcept(), + CSE->getConceptNameInfo().getLoc(), Out, + /*DefaultArgs=*/{}, + /*PartialTemplateArgs=*/false, CTAI, + /*UpdateArgsWithConversions=*/false)) + return true; + auto TemplateArgs = *MLTAL; + TemplateArgs.replaceOutermostTemplateArguments( + TemplateArgs.getAssociatedDecl(0).first, CTAI.SugaredConverted); + return SubstituteParameterMappings(SemaRef, &TemplateArgs, ArgsAsWritten, + InFoldExpr) + .substitute(CC.getNormalizedConstraint()); } -NormalizedConstraint &NormalizedConstraint::getRHS() const { - assert(isCompound() && "getRHS called on a non-compound constraint."); - return cast<CompoundConstraint>(Constraint).getPointer()->RHS; +bool SubstituteParameterMappings::substitute(NormalizedConstraint &N) { + switch (N.getKind()) { + case NormalizedConstraint::ConstraintKind::Atomic: { + if (!MLTAL) { + assert(!ArgsAsWritten); + return false; + } + return substitute(static_cast<NormalizedConstraintWithParamMapping &>(N)); + } + case NormalizedConstraint::ConstraintKind::FoldExpanded: { + auto &FE = static_cast<FoldExpandedConstraint &>(N); + if (!MLTAL) { + llvm::SaveAndRestore _1(InFoldExpr, true); + assert(!ArgsAsWritten); + return substitute(FE.getNormalizedPattern()); + } + Sema::ArgPackSubstIndexRAII _(SemaRef, std::nullopt); + substitute(static_cast<NormalizedConstraintWithParamMapping &>(FE)); + return SubstituteParameterMappings(SemaRef, /*InFoldExpr=*/true) + .substitute(FE.getNormalizedPattern()); + } + case NormalizedConstraint::ConstraintKind::ConceptId: { + auto &CC = static_cast<ConceptIdConstraint &>(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<CompoundConstraint &>(N); + if (substitute(Compound.getLHS())) + return true; + return substitute(Compound.getRHS()); + } + } } -std::optional<NormalizedConstraint> -NormalizedConstraint::fromAssociatedConstraints( +} // namespace + +NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints( Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) { assert(ACs.size() != 0); - auto Conjunction = fromConstraintExpr(S, D, ACs[0].ConstraintExpr); + auto *Conjunction = + fromConstraintExpr(S, D, ACs[0].ConstraintExpr, ACs[0].ArgPackSubstIndex); if (!Conjunction) - return std::nullopt; + return nullptr; for (unsigned I = 1; I < ACs.size(); ++I) { - auto Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr); + auto *Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr, + ACs[I].ArgPackSubstIndex); if (!Next) - return std::nullopt; - *Conjunction = NormalizedConstraint(S.Context, std::move(*Conjunction), - std::move(*Next), CCK_Conjunction); + return nullptr; + Conjunction = CompoundConstraint::CreateConjunction(S.getASTContext(), + Conjunction, Next); } return Conjunction; } -std::optional<NormalizedConstraint> -NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, - const Expr *E) { +NormalizedConstraint *NormalizedConstraint::fromConstraintExpr( + Sema &S, const NamedDecl *D, const Expr *E, UnsignedOrNone SubstIndex) { assert(E != nullptr); // C++ [temp.constr.normal]p1.1 @@ -1597,23 +2170,29 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, // [...] E = E->IgnoreParenImpCasts(); + llvm::FoldingSetNodeID ID; + if (D && DiagRecursiveConstraintEval(S, ID, D, E)) { + return nullptr; + } + SatisfactionStackRAII StackRAII(S, D, ID); + // C++2a [temp.param]p4: // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). // Fold expression is considered atomic constraints per current wording. // See http://cplusplus.github.io/concepts-ts/ts-active.html#28 if (LogicalBinOp BO = E) { - auto LHS = fromConstraintExpr(S, D, BO.getLHS()); + auto *LHS = fromConstraintExpr(S, D, BO.getLHS(), SubstIndex); if (!LHS) - return std::nullopt; - auto RHS = fromConstraintExpr(S, D, BO.getRHS()); + return nullptr; + auto *RHS = fromConstraintExpr(S, D, BO.getRHS(), SubstIndex); if (!RHS) - return std::nullopt; + return nullptr; - return NormalizedConstraint(S.Context, std::move(*LHS), std::move(*RHS), - BO.isAnd() ? CCK_Conjunction : CCK_Disjunction); + return CompoundConstraint::Create( + S.Context, LHS, BO.isAnd() ? CCK_Conjunction : CCK_Disjunction, RHS); } else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) { - const NormalizedConstraint *SubNF; + NormalizedConstraint *SubNF; { Sema::InstantiatingTemplate Inst( S, CSE->getExprLoc(), @@ -1621,7 +2200,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, // FIXME: improve const-correctness of InstantiatingTemplate const_cast<NamedDecl *>(D), CSE->getSourceRange()); if (Inst.isInvalid()) - return std::nullopt; + return nullptr; // C++ [temp.constr.normal]p1.1 // [...] // The normal form of an id-expression of the form C<A1, A2, ..., AN>, @@ -1631,20 +2210,21 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, // constraint. If any such substitution results in an invalid type or // expression, the program is ill-formed; no diagnostic is required. // [...] - ConceptDecl *CD = CSE->getNamedConcept(); - SubNF = S.getNormalizedAssociatedConstraints( - CD, AssociatedConstraint(CD->getConstraintExpr())); + + // Use canonical declarations to merge ConceptDecls across + // different modules. + ConceptDecl *CD = CSE->getNamedConcept()->getCanonicalDecl(); + SubNF = NormalizedConstraint::fromAssociatedConstraints( + S, CD, AssociatedConstraint(CD->getConstraintExpr(), SubstIndex)); + if (!SubNF) - return std::nullopt; + return nullptr; } - std::optional<NormalizedConstraint> New; - New.emplace(S.Context, *SubNF); - - if (substituteParameterMappings(S, *New, CSE)) - return std::nullopt; + return ConceptIdConstraint::Create(S.getASTContext(), + CSE->getConceptReference(), SubNF, D, + CSE, SubstIndex); - return New; } else if (auto *FE = dyn_cast<const CXXFoldExpr>(E); FE && S.getLangOpts().CPlusPlus26 && (FE->getOperator() == BinaryOperatorKind::BO_LAnd || @@ -1658,31 +2238,61 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D, : FoldExpandedConstraint::FoldOperatorKind::Or; if (FE->getInit()) { - auto LHS = fromConstraintExpr(S, D, FE->getLHS()); - auto RHS = fromConstraintExpr(S, D, FE->getRHS()); + auto *LHS = fromConstraintExpr(S, D, FE->getLHS(), SubstIndex); + auto *RHS = fromConstraintExpr(S, D, FE->getRHS(), SubstIndex); if (!LHS || !RHS) - return std::nullopt; + return nullptr; if (FE->isRightFold()) - RHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{ - Kind, std::move(*RHS), FE->getPattern()}}; + LHS = FoldExpandedConstraint::Create(S.getASTContext(), + FE->getPattern(), D, Kind, LHS); else - LHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{ - Kind, std::move(*LHS), FE->getPattern()}}; - - return NormalizedConstraint( - S.Context, std::move(*LHS), std::move(*RHS), - FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction - : CCK_Disjunction); + RHS = FoldExpandedConstraint::Create(S.getASTContext(), + FE->getPattern(), D, Kind, RHS); + + return CompoundConstraint::Create( + S.getASTContext(), LHS, + (FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction + : CCK_Disjunction), + RHS); } - auto Sub = fromConstraintExpr(S, D, FE->getPattern()); + auto *Sub = fromConstraintExpr(S, D, FE->getPattern(), SubstIndex); if (!Sub) - return std::nullopt; - return NormalizedConstraint{new (S.Context) FoldExpandedConstraint{ - Kind, std::move(*Sub), FE->getPattern()}}; + return nullptr; + return FoldExpandedConstraint::Create(S.getASTContext(), FE->getPattern(), + D, Kind, Sub); } + return AtomicConstraint::Create(S.getASTContext(), E, D, SubstIndex); +} - return NormalizedConstraint{new (S.Context) AtomicConstraint(E, D)}; +const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints( + ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq, + ArrayRef<AssociatedConstraint> AssociatedConstraints) { + if (!ConstrainedDeclOrNestedReq) { + auto *Normalized = NormalizedConstraint::fromAssociatedConstraints( + *this, nullptr, AssociatedConstraints); + if (!Normalized || + SubstituteParameterMappings(*this).substitute(*Normalized)) + return nullptr; + + return Normalized; + } + + // FIXME: ConstrainedDeclOrNestedReq is never a NestedRequirement! + const NamedDecl *ND = + ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>(); + auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq); + if (CacheEntry == NormalizationCache.end()) { + auto *Normalized = NormalizedConstraint::fromAssociatedConstraints( + *this, ND, AssociatedConstraints); + CacheEntry = + NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized) + .first; + if (!Normalized || + SubstituteParameterMappings(*this).substitute(*Normalized)) + return nullptr; + } + return CacheEntry->second; } bool FoldExpandedConstraint::AreCompatibleForSubsumption( @@ -1693,8 +2303,10 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption( // if their respective constraints both contain an equivalent unexpanded pack. llvm::SmallVector<UnexpandedParameterPack> APacks, BPacks; - Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.Pattern), APacks); - Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks); + Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.getPattern()), + APacks); + Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.getPattern()), + BPacks); for (const UnexpandedParameterPack &APack : APacks) { auto ADI = getDepthAndIndex(APack); @@ -1788,7 +2400,7 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic( const AtomicConstraint &B) { if (!A.hasMatchingParameterMapping(Context, B)) return false; - const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr; + const Expr *EA = A.getConstraintExpr(), *EB = B.getConstraintExpr(); if (EA == EB) return true; @@ -1841,24 +2453,6 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic( return true; } -NormalizedConstraint::CompoundConstraintKind -NormalizedConstraint::getCompoundKind() const { - assert(isCompound() && "getCompoundKind on a non-compound constraint.."); - return cast<CompoundConstraint>(Constraint).getInt(); -} - -AtomicConstraint *NormalizedConstraint::getAtomicConstraint() const { - assert(isAtomic() && "getAtomicConstraint called on non-atomic constraint."); - return cast<AtomicConstraint *>(Constraint); -} - -FoldExpandedConstraint * -NormalizedConstraint::getFoldExpandedConstraint() const { - assert(isFoldExpanded() && - "getFoldExpandedConstraint called on non-fold-expanded constraint."); - return cast<FoldExpandedConstraint *>(Constraint); -} - // // // ------------------------ Subsumption ----------------------------------- @@ -1874,8 +2468,8 @@ uint16_t SubsumptionChecker::getNewLiteralId() { return NextID++; } -auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal { - auto &Elems = AtomicMap[Ori->ConstraintExpr]; +auto SubsumptionChecker::find(const AtomicConstraint *Ori) -> Literal { + auto &Elems = AtomicMap[Ori->getConstraintExpr()]; // C++ [temp.constr.order] p2 // - an atomic constraint A subsumes another atomic constraint B // if and only if the A and B are identical [...] @@ -1891,13 +2485,16 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal { // subsumes another, their literal will be the same llvm::FoldingSetNodeID ID; - const auto &Mapping = Ori->ParameterMapping; - ID.AddBoolean(Mapping.has_value()); - if (Mapping) { - for (const TemplateArgumentLoc &TAL : *Mapping) { - SemaRef.getASTContext() - .getCanonicalTemplateArgument(TAL.getArgument()) - .Profile(ID, SemaRef.getASTContext()); + 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()); } } auto It = Elems.find(ID); @@ -1912,11 +2509,11 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal { return It->getSecond().ID; } -auto SubsumptionChecker::find(FoldExpandedConstraint *Ori) -> Literal { - auto &Elems = FoldMap[Ori->Pattern]; +auto SubsumptionChecker::find(const FoldExpandedConstraint *Ori) -> Literal { + auto &Elems = FoldMap[Ori->getPattern()]; FoldExpendedConstraintKey K; - K.Kind = Ori->Kind; + K.Kind = Ori->getFoldOperator(); auto It = llvm::find_if(Elems, [&K](const FoldExpendedConstraintKey &Other) { return K.Kind == Other.Kind; @@ -1960,38 +2557,47 @@ FormulaType SubsumptionChecker::Normalize(const NormalizedConstraint &NC) { AddUniqueClauseToFormula(Res, std::move(C)); }; - if (NC.isAtomic()) - return {{find(NC.getAtomicConstraint())}}; + switch (NC.getKind()) { - if (NC.isFoldExpanded()) - return {{find(NC.getFoldExpandedConstraint())}}; + case NormalizedConstraint::ConstraintKind::Atomic: + return {{find(&static_cast<const AtomicConstraint &>(NC))}}; - FormulaType Left, Right; - SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] { - Left = Normalize<FormulaType>(NC.getLHS()); - Right = Normalize<FormulaType>(NC.getRHS()); - }); + case NormalizedConstraint::ConstraintKind::FoldExpanded: + return {{find(&static_cast<const FoldExpandedConstraint &>(NC))}}; - if (NC.getCompoundKind() == FormulaType::Kind) { - auto SizeLeft = Left.size(); - Res = std::move(Left); - Res.reserve(SizeLeft + Right.size()); - std::for_each(std::make_move_iterator(Right.begin()), - std::make_move_iterator(Right.end()), Add); - return Res; - } + case NormalizedConstraint::ConstraintKind::ConceptId: + return Normalize<FormulaType>( + static_cast<const ConceptIdConstraint &>(NC).getNormalizedConstraint()); + + case NormalizedConstraint::ConstraintKind::Compound: { + const auto &Compound = static_cast<const CompoundConstraint &>(NC); + FormulaType Left, Right; + SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] { + Left = Normalize<FormulaType>(Compound.getLHS()); + Right = Normalize<FormulaType>(Compound.getRHS()); + }); + + if (Compound.getCompoundKind() == FormulaType::Kind) { + Res = std::move(Left); + Res.reserve(Left.size() + Right.size()); + std::for_each(std::make_move_iterator(Right.begin()), + std::make_move_iterator(Right.end()), Add); + return Res; + } - Res.reserve(Left.size() * Right.size()); - for (const auto <ransform : Left) { - for (const auto &RTransform : Right) { - Clause Combined; - Combined.reserve(LTransform.size() + RTransform.size()); - llvm::append_range(Combined, LTransform); - llvm::append_range(Combined, RTransform); - Add(std::move(Combined)); + Res.reserve(Left.size() * Right.size()); + for (const auto <ransform : Left) { + for (const auto &RTransform : Right) { + Clause Combined; + Combined.reserve(LTransform.size() + RTransform.size()); + llvm::copy(LTransform, std::back_inserter(Combined)); + llvm::copy(RTransform, std::back_inserter(Combined)); + Add(std::move(Combined)); + } } + return Res; + } } - return Res; } void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) { @@ -2006,12 +2612,12 @@ std::optional<bool> SubsumptionChecker::Subsumes( const NamedDecl *DP, ArrayRef<AssociatedConstraint> P, const NamedDecl *DQ, ArrayRef<AssociatedConstraint> Q) { const NormalizedConstraint *PNormalized = - getNormalizedAssociatedConstraints(SemaRef, DP, P); + SemaRef.getNormalizedAssociatedConstraints(DP, P); if (!PNormalized) return std::nullopt; const NormalizedConstraint *QNormalized = - getNormalizedAssociatedConstraints(SemaRef, DQ, Q); + SemaRef.getNormalizedAssociatedConstraints(DQ, Q); if (!QNormalized) return std::nullopt; @@ -2061,9 +2667,9 @@ bool SubsumptionChecker::Subsumes(const FoldExpandedConstraint *A, // constraint B if they are compatible for subsumption, have the same // fold-operator, and the constraint of A subsumes that of B. bool DoesSubsume = - A->Kind == B->Kind && + A->getFoldOperator() == B->getFoldOperator() && FoldExpandedConstraint::AreCompatibleForSubsumption(*A, *B) && - Subsumes(&A->Constraint, &B->Constraint); + Subsumes(&A->getNormalizedPattern(), &B->getNormalizedPattern()); It = FoldSubsumptionCache.try_emplace(std::move(Key), DoesSubsume).first; } return It->second; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 16d42d2..d27f767 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17876,13 +17876,15 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, findFailedBooleanCondition(Converted.get()); if (const auto *ConceptIDExpr = dyn_cast_or_null<ConceptSpecializationExpr>(InnerCond)) { - // Drill down into concept specialization expressions to see why they - // weren't satisfied. - Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed) - << !HasMessage << Msg.str() << AssertExpr->getSourceRange(); - ConstraintSatisfaction Satisfaction; - if (!CheckConstraintSatisfaction(ConceptIDExpr, Satisfaction)) - DiagnoseUnsatisfiedConstraint(Satisfaction); + const ASTConstraintSatisfaction &Satisfaction = + ConceptIDExpr->getSatisfaction(); + if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) { + Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed) + << !HasMessage << Msg.str() << AssertExpr->getSourceRange(); + // Drill down into concept specialization expressions to see why they + // weren't satisfied. + DiagnoseUnsatisfiedConstraint(ConceptIDExpr); + } } else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) && !isa<IntegerLiteral>(InnerCond)) { Diag(InnerCond->getBeginLoc(), diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 779ccf5..0fe242dce 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1251,6 +1251,10 @@ Sema::CXXThisScopeRAII::CXXThisScopeRAII(Sema &S, else Record = cast<CXXRecordDecl>(ContextDecl); + // 'this' never refers to the lambda class itself. + if (Record->isLambda()) + return; + QualType T = S.Context.getCanonicalTagType(Record); T = S.getASTContext().getQualifiedType(T, CXXThisTypeQuals); @@ -7931,21 +7935,27 @@ Sema::BuildExprRequirement( // be satisfied. TemplateParameterList *TPL = ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); - QualType MatchedType = - Context.getReferenceQualifiedType(E).getCanonicalType(); + QualType MatchedType = Context.getReferenceQualifiedType(E); llvm::SmallVector<TemplateArgument, 1> Args; Args.push_back(TemplateArgument(MatchedType)); auto *Param = cast<TemplateTypeParmDecl>(TPL->getParam(0)); - MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false); + MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/true); MLTAL.addOuterRetainedLevels(TPL->getDepth()); const TypeConstraint *TC = Param->getTypeConstraint(); assert(TC && "Type Constraint cannot be null here"); auto *IDC = TC->getImmediatelyDeclaredConstraint(); assert(IDC && "ImmediatelyDeclaredConstraint can't be null here."); ExprResult Constraint = SubstExpr(IDC, MLTAL); - if (Constraint.isInvalid()) { + bool HasError = Constraint.isInvalid(); + if (!HasError) { + SubstitutedConstraintExpr = + cast<ConceptSpecializationExpr>(Constraint.get()); + if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors) + HasError = true; + } + if (HasError) { return new (Context) concepts::ExprRequirement( createSubstDiagAt(IDC->getExprLoc(), [&](llvm::raw_ostream &OS) { @@ -7954,8 +7964,6 @@ Sema::BuildExprRequirement( }), IsSimple, NoexceptLoc, ReturnTypeRequirement); } - SubstitutedConstraintExpr = - cast<ConceptSpecializationExpr>(Constraint.get()); if (!SubstitutedConstraintExpr->isSatisfied()) Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied; } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index c971293..0d0d2c0 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -8219,8 +8219,8 @@ ExprResult InitializationSequence::Perform(Sema &S, // InitializeTemporary entity for our target type. QualType Ty = Step->Type; bool IsTemporary = !S.Context.hasSameType(Entity.getType(), Ty); - InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(Ty); - InitializedEntity InitEntity = IsTemporary ? TempEntity : Entity; + InitializedEntity InitEntity = + IsTemporary ? InitializedEntity::InitializeTemporary(Ty) : Entity; InitListChecker PerformInitList(S, InitEntity, InitList, Ty, /*VerifyOnly=*/false, /*TreatUnavailableAsInvalid=*/false); @@ -8242,7 +8242,6 @@ ExprResult InitializationSequence::Perform(Sema &S, InitListExpr *StructuredInitList = PerformInitList.getFullyStructuredList(); - CurInit.get(); CurInit = shouldBindAsTemporary(InitEntity) ? S.MaybeBindToTemporary(StructuredInitList) : StructuredInitList; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index ea5c4265..b870114 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -804,7 +804,7 @@ clang::MakeDeductionFailureInfo(ASTContext &Context, case TemplateDeductionResult::ConstraintsNotSatisfied: { CNSInfo *Saved = new (Context) CNSInfo; Saved->TemplateArgs = Info.takeSugared(); - Saved->Satisfaction = Info.AssociatedConstraintsSatisfaction; + Saved->Satisfaction = std::move(Info.AssociatedConstraintsSatisfaction); Result.Data = Saved; break; } @@ -852,6 +852,7 @@ void DeductionFailureInfo::Destroy() { case TemplateDeductionResult::ConstraintsNotSatisfied: // FIXME: Destroy the template argument list? + static_cast<CNSInfo *>(Data)->Satisfaction.~ConstraintSatisfaction(); Data = nullptr; if (PartialDiagnosticAt *Diag = getSFINAEDiagnostic()) { Diag->~PartialDiagnosticAt(); @@ -12739,7 +12740,8 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, << (unsigned)FnKindPair.first << (unsigned)ocs_non_template << FnDesc /* Ignored */; ConstraintSatisfaction Satisfaction; - if (S.CheckFunctionConstraints(Fn, Satisfaction)) + if (S.CheckFunctionConstraints(Fn, Satisfaction, SourceLocation(), + /*ForOverloadResolution=*/true)) break; S.DiagnoseUnsatisfiedConstraint(Satisfaction); } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 2bf1511..dcf2876 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -9,6 +9,7 @@ //===----------------------------------------------------------------------===// #include "TreeTransform.h" +#include "clang/AST/ASTConcept.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -1222,8 +1223,9 @@ static ExprResult formImmediatelyDeclaredConstraint( if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) { ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId( SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, - /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD, - &ConstraintArgs); + /*FoundDecl=*/FoundDecl ? FoundDecl : CD, CD, &ConstraintArgs, + /*DoCheckConstraintSatisfaction=*/ + !S.inParameterMappingSubstitution()); } // We have a template template parameter else { @@ -4850,13 +4852,11 @@ void Sema::diagnoseMissingTemplateArguments(const CXXScopeSpec &SS, diagnoseMissingTemplateArguments(Name, Loc); } -ExprResult -Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - const DeclarationNameInfo &ConceptNameInfo, - NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, - const TemplateArgumentListInfo *TemplateArgs) { +ExprResult Sema::CheckConceptTemplateId( + const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl, + TemplateDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs, + bool DoCheckConstraintSatisfaction) { assert(NamedConcept && "A concept template id without a template?"); if (NamedConcept->isInvalidDecl()) @@ -4873,33 +4873,48 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc()); + // There's a bug with CTAI.CanonicalConverted. + // If the template argument contains a DependentDecltypeType that includes a + // TypeAliasType, and the same written type had occurred previously in the + // source, then the DependentDecltypeType would be canonicalized to that + // previous type which would mess up the substitution. + // FIXME: Reland https://github.com/llvm/llvm-project/pull/101782 properly! auto *CSD = ImplicitConceptSpecializationDecl::Create( Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(), - CTAI.CanonicalConverted); + CTAI.SugaredConverted); ConstraintSatisfaction Satisfaction; bool AreArgsDependent = TemplateSpecializationType::anyDependentTemplateArguments( - *TemplateArgs, CTAI.CanonicalConverted); - MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.CanonicalConverted, + *TemplateArgs, CTAI.SugaredConverted); + MultiLevelTemplateArgumentList MLTAL(NamedConcept, CTAI.SugaredConverted, /*Final=*/false); - LocalInstantiationScope Scope(*this); - - EnterExpressionEvaluationContext EECtx{ - *this, ExpressionEvaluationContext::Unevaluated, CSD}; - - if (!AreArgsDependent && - CheckConstraintSatisfaction( - NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()), - MLTAL, - SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(), - TemplateArgs->getRAngleLoc()), - Satisfaction)) - return ExprError(); auto *CL = ConceptReference::Create( Context, SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{}, TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept, ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs)); + + bool Error = false; + if (const auto *Concept = dyn_cast<ConceptDecl>(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); } @@ -5217,10 +5232,11 @@ bool Sema::CheckTemplateTypeArgument( } default: { // We allow instantiating a template with template argument packs when - // building deduction guides. + // building deduction guides or mapping constraint template parameters. if (Arg.getKind() == TemplateArgument::Pack && - CodeSynthesisContexts.back().Kind == - Sema::CodeSynthesisContext::BuildingDeductionGuides) { + (CodeSynthesisContexts.back().Kind == + Sema::CodeSynthesisContext::BuildingDeductionGuides || + inParameterMappingSubstitution())) { SugaredConverted.push_back(Arg); CanonicalConverted.push_back(Arg); return false; @@ -5813,6 +5829,20 @@ bool Sema::CheckTemplateArgumentList( TemplateArgumentListInfo &TemplateArgs, const DefaultArguments &DefaultArgs, bool PartialTemplateArgs, CheckTemplateArgumentInfo &CTAI, bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied) { + return CheckTemplateArgumentList( + Template, GetTemplateParameterList(Template), TemplateLoc, TemplateArgs, + DefaultArgs, PartialTemplateArgs, CTAI, UpdateArgsWithConversions, + ConstraintsNotSatisfied); +} + +/// Check that the given template argument list is well-formed +/// for specializing the given template. +bool Sema::CheckTemplateArgumentList( + TemplateDecl *Template, TemplateParameterList *Params, + SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs, + const DefaultArguments &DefaultArgs, bool PartialTemplateArgs, + CheckTemplateArgumentInfo &CTAI, bool UpdateArgsWithConversions, + bool *ConstraintsNotSatisfied) { if (ConstraintsNotSatisfied) *ConstraintsNotSatisfied = false; @@ -5822,8 +5852,6 @@ bool Sema::CheckTemplateArgumentList( // template. TemplateArgumentListInfo NewArgs = TemplateArgs; - TemplateParameterList *Params = GetTemplateParameterList(Template); - SourceLocation RAngleLoc = NewArgs.getRAngleLoc(); // C++23 [temp.arg.general]p1: @@ -6163,11 +6191,12 @@ bool Sema::CheckTemplateArgumentList( CXXThisScopeRAII Scope(*this, RD, ThisQuals, RD != nullptr); MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( - Template, NewContext, /*Final=*/false, CTAI.CanonicalConverted, + Template, NewContext, /*Final=*/true, CTAI.SugaredConverted, /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConceptInstantiation=*/true); - if (EnsureTemplateArgumentListConstraints( + if (!isa<ConceptDecl>(Template) && + EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { if (ConstraintsNotSatisfied) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index f6ee745..6bba505 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 = CanonicalDeducedArgs; + Innermost = SugaredDeducedArgs; 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, CanonicalDeducedArgs); + MLTAL.replaceInnermostTemplateArguments(Template, SugaredDeducedArgs); if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, Info.getLocation(), @@ -3995,11 +3995,12 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (CheckFunctionTemplateConstraints( Info.getLocation(), FunctionTemplate->getCanonicalDecl()->getTemplatedDecl(), - CTAI.CanonicalConverted, Info.AssociatedConstraintsSatisfaction)) + CTAI.SugaredConverted, Info.AssociatedConstraintsSatisfaction)) return TemplateDeductionResult::MiscellaneousDeductionFailure; if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) { - Info.reset(Info.takeSugared(), TemplateArgumentList::CreateCopy( - Context, CTAI.CanonicalConverted)); + Info.reset( + TemplateArgumentList::CreateCopy(Context, CTAI.SugaredConverted), + Info.takeCanonical()); return TemplateDeductionResult::ConstraintsNotSatisfied; } } @@ -5167,8 +5168,8 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, CTAI)) return true; - MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.CanonicalConverted, - /*Final=*/false); + MultiLevelTemplateArgumentList MLTAL(Concept, CTAI.SugaredConverted, + /*Final=*/true); // Build up an EvaluationContext with an ImplicitConceptSpecializationDecl so // that the template arguments of the constraint can be preserved. For // example: @@ -5182,7 +5183,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, S, Sema::ExpressionEvaluationContext::Unevaluated, ImplicitConceptSpecializationDecl::Create( S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(), - CTAI.CanonicalConverted)); + CTAI.SugaredConverted)); if (S.CheckConstraintSatisfaction( Concept, AssociatedConstraint(Concept->getConstraintExpr()), MLTAL, TypeLoc.getLocalSourceRange(), Satisfaction)) @@ -6676,10 +6677,11 @@ namespace { struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor { llvm::SmallBitVector &Used; unsigned Depth; + bool VisitDeclRefTypes = true; - MarkUsedTemplateParameterVisitor(llvm::SmallBitVector &Used, - unsigned Depth) - : Used(Used), Depth(Depth) { } + MarkUsedTemplateParameterVisitor(llvm::SmallBitVector &Used, unsigned Depth, + bool VisitDeclRefTypes = true) + : Used(Used), Depth(Depth), VisitDeclRefTypes(VisitDeclRefTypes) {} bool VisitTemplateTypeParmType(TemplateTypeParmType *T) override { if (T->getDepth() == Depth) @@ -6700,6 +6702,8 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor { if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(E->getDecl())) if (NTTP->getDepth() == Depth) Used[NTTP->getIndex()] = true; + if (VisitDeclRefTypes) + DynamicRecursiveASTVisitor::TraverseType(E->getType()); return true; } @@ -7043,10 +7047,13 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, break; case Type::UnaryTransform: - if (!OnlyDeduced) - MarkUsedTemplateParameters(Ctx, - cast<UnaryTransformType>(T)->getUnderlyingType(), - OnlyDeduced, Depth, Used); + if (!OnlyDeduced) { + auto *UTT = cast<UnaryTransformType>(T); + auto Next = UTT->getUnderlyingType(); + if (Next.isNull()) + Next = UTT->getBaseType(); + MarkUsedTemplateParameters(Ctx, Next, OnlyDeduced, Depth, Used); + } break; case Type::PackExpansion: @@ -7146,6 +7153,12 @@ 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<Expr *>(E)); +} + void Sema::MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs, bool OnlyDeduced, unsigned Depth, @@ -7171,6 +7184,14 @@ void Sema::MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs, /*OnlyDeduced=*/false, Depth, Used); } +void Sema::MarkUsedTemplateParameters( + ArrayRef<TemplateArgumentLoc> TemplateArgs, unsigned Depth, + llvm::SmallBitVector &Used) { + for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I) + ::MarkUsedTemplateParameters(Context, TemplateArgs[I].getArgument(), + /*OnlyDeduced=*/false, Depth, Used); +} + void Sema::MarkDeducedTemplateParameters( ASTContext &Ctx, const FunctionTemplateDecl *FunctionTemplate, llvm::SmallBitVector &Deduced) { diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp index fe673ea..9a61888 100644 --- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp +++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp @@ -1171,17 +1171,46 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef, Args.addOuterTemplateArguments(TransformedDeducedAliasArgs); for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) { const auto &D = DeduceResults[Index]; + auto *TP = F->getTemplateParameters()->getParam(Index); if (IsNonDeducedArgument(D)) { // 2): Non-deduced template parameters would be substituted later. continue; } TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{}); - TemplateArgumentLoc Output; - if (!SemaRef.SubstTemplateArgument(Input, Args, Output)) { - assert(TemplateArgsForBuildingFPrime[Index].isNull() && - "InstantiatedArgs must be null before setting"); - TemplateArgsForBuildingFPrime[Index] = Output.getArgument(); + TemplateArgumentListInfo Output; + if (SemaRef.SubstTemplateArguments(Input, Args, Output)) + return nullptr; + assert(TemplateArgsForBuildingFPrime[Index].isNull() && + "InstantiatedArgs must be null before setting"); + // CheckTemplateArgument is necessary for NTTP initializations. + // FIXME: We may want to call CheckTemplateArguments instead, but we cannot + // match packs as usual, since packs can appear in the middle of the + // parameter list of a synthesized CTAD guide. See also the FIXME in + // test/SemaCXX/cxx20-ctad-type-alias.cpp:test25. + Sema::CheckTemplateArgumentInfo CTAI; + if (Input.getArgument().getKind() == TemplateArgument::Pack) { + for (auto TA : Output.arguments()) { + if (SemaRef.CheckTemplateArgument( + TP, TA, F, F->getLocation(), F->getLocation(), + /*ArgumentPackIndex=*/-1, CTAI, + Sema::CheckTemplateArgumentKind::CTAK_Specified)) + return nullptr; + } + // We will substitute the non-deduced template arguments with these + // transformed (unpacked at this point) arguments, where that substitution + // requires a pack for the corresponding parameter packs. + TemplateArgsForBuildingFPrime[Index] = + TemplateArgument::CreatePackCopy(Context, CTAI.SugaredConverted); + } else { + assert(Output.arguments().size() == 1); + TemplateArgumentLoc Transformed = Output.arguments()[0]; + if (SemaRef.CheckTemplateArgument( + TP, Transformed, F, F->getLocation(), F->getLocation(), + /*ArgumentPackIndex=*/-1, CTAI, + Sema::CheckTemplateArgumentKind::CTAK_Specified)) + return nullptr; + TemplateArgsForBuildingFPrime[Index] = CTAI.SugaredConverted[0]; } } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index f1c9c5c..1f762ca 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -628,9 +628,14 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Inst.InstantiationRange = InstantiationRange; Inst.InConstraintSubstitution = Inst.Kind == CodeSynthesisContext::ConstraintSubstitution; - if (!SemaRef.CodeSynthesisContexts.empty()) + Inst.InParameterMappingSubstitution = + Inst.Kind == CodeSynthesisContext::ParameterMappingSubstitution; + if (!SemaRef.CodeSynthesisContexts.empty()) { Inst.InConstraintSubstitution |= SemaRef.CodeSynthesisContexts.back().InConstraintSubstitution; + Inst.InParameterMappingSubstitution |= + SemaRef.CodeSynthesisContexts.back().InParameterMappingSubstitution; + } Invalid = SemaRef.pushCodeSynthesisContext(Inst); if (!Invalid) { @@ -1375,6 +1380,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { // Template Instantiation for Types //===----------------------------------------------------------------------===/ namespace { + class TemplateInstantiator : public TreeTransform<TemplateInstantiator> { const MultiLevelTemplateArgumentList &TemplateArgs; SourceLocation Loc; @@ -1387,7 +1393,11 @@ namespace { // Whether an incomplete substituion should be treated as an error. bool BailOutOnIncomplete; - private: + // 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; + // CWG2770: Function parameters should be instantiated when they are // needed by a satisfaction check of an atomic constraint or // (recursively) by another function parameter. @@ -1410,6 +1420,17 @@ 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. /// @@ -1444,7 +1465,8 @@ namespace { bool &ShouldExpand, bool &RetainExpansion, UnsignedOrNone &NumExpansions) { if (SemaRef.CurrentInstantiationScope && - SemaRef.inConstraintSubstitution()) { + (SemaRef.inConstraintSubstitution() || + SemaRef.inParameterMappingSubstitution())) { for (UnexpandedParameterPack ParmPack : Unexpanded) { NamedDecl *VD = ParmPack.first.dyn_cast<NamedDecl *>(); if (auto *PVD = dyn_cast_if_present<ParmVarDecl>(VD); @@ -1465,10 +1487,10 @@ namespace { TemplateArgument ForgetPartiallySubstitutedPack() { TemplateArgument Result; - if (NamedDecl *PartialPack - = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){ - MultiLevelTemplateArgumentList &TemplateArgs - = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs); + if (NamedDecl *PartialPack = SemaRef.CurrentInstantiationScope + ->getPartiallySubstitutedPack()) { + MultiLevelTemplateArgumentList &TemplateArgs = + const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs); unsigned Depth, Index; std::tie(Depth, Index) = getDepthAndIndex(PartialPack); if (TemplateArgs.hasTemplateArgument(Depth, Index)) { @@ -1488,10 +1510,10 @@ namespace { if (Arg.isNull()) return; - if (NamedDecl *PartialPack - = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){ - MultiLevelTemplateArgumentList &TemplateArgs - = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs); + if (NamedDecl *PartialPack = SemaRef.CurrentInstantiationScope + ->getPartiallySubstitutedPack()) { + MultiLevelTemplateArgumentList &TemplateArgs = + const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs); unsigned Depth, Index; std::tie(Depth, Index) = getDepthAndIndex(PartialPack); TemplateArgs.setArgument(Depth, Index, Arg); @@ -1508,9 +1530,9 @@ namespace { std::move(New); return Old; } + void RememberSubstitution(MultiLevelTemplateArgumentList Old) { - const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs) = - std::move(Old); + const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs) = Old; } TemplateArgument @@ -1691,6 +1713,24 @@ namespace { return inherited::TransformTemplateArgument(Input, Output, Uneval); } + // This has to be here to allow its overload. + ExprResult RebuildPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc, + UnsignedOrNone NumExpansions) { + return inherited::RebuildPackExpansion(Pattern, EllipsisLoc, + NumExpansions); + } + + TemplateArgumentLoc RebuildPackExpansion(TemplateArgumentLoc Pattern, + SourceLocation EllipsisLoc, + UnsignedOrNone NumExpansions) { + // We don't rewrite a PackExpansion type when we want to normalize a + // CXXFoldExpr constraint. We'll expand it when evaluating the constraint. + if (BuildPackExpansionTypes) + return inherited::RebuildPackExpansion(Pattern, EllipsisLoc, + NumExpansions); + return Pattern; + } + using TreeTransform::TransformTemplateSpecializationType; QualType TransformTemplateSpecializationType(TypeLocBuilder &TLB, @@ -1961,7 +2001,8 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) { if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(D); PVD && SemaRef.CurrentInstantiationScope && - SemaRef.inConstraintSubstitution() && + (SemaRef.inConstraintSubstitution() || + SemaRef.inParameterMappingSubstitution()) && maybeInstantiateFunctionParameterToScope(PVD)) return nullptr; @@ -2759,18 +2800,29 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) { concepts::NestedRequirement * TemplateInstantiator::TransformNestedRequirement( concepts::NestedRequirement *Req) { - if (!Req->isDependent() && !AlwaysRebuild()) - return Req; + + ASTContext &C = SemaRef.Context; + + Expr *Constraint = Req->getConstraintExpr(); + ConstraintSatisfaction Satisfaction; + + auto NestedReqWithDiag = [&C, this](Expr *E, + ConstraintSatisfaction Satisfaction) { + Satisfaction.IsSatisfied = false; + SmallString<128> Entity; + llvm::raw_svector_ostream OS(Entity); + E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); + return new (C) concepts::NestedRequirement( + SemaRef.Context, C.backupStr(Entity), std::move(Satisfaction)); + }; + if (Req->hasInvalidConstraint()) { if (AlwaysRebuild()) return RebuildNestedRequirement(Req->getInvalidConstraintEntity(), Req->getConstraintSatisfaction()); return Req; } - Sema::InstantiatingTemplate ReqInst(SemaRef, - Req->getConstraintExpr()->getBeginLoc(), Req, - Sema::InstantiatingTemplate::ConstraintsCheck{}, - Req->getConstraintExpr()->getSourceRange()); + if (!getEvaluateConstraints()) { ExprResult TransConstraint = TransformExpr(Req->getConstraintExpr()); if (TransConstraint.isInvalid() || !TransConstraint.get()) @@ -2783,45 +2835,45 @@ TemplateInstantiator::TransformNestedRequirement( SemaRef.Context, TransConstraint.get(), Satisfaction); } - ExprResult TransConstraint; - ConstraintSatisfaction Satisfaction; - TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc()); + bool Success; + Expr *NewConstraint; + TemplateDeductionInfo Info(Constraint->getBeginLoc()); { EnterExpressionEvaluationContext ContextRAII( SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); - Sema::SFINAETrap Trap(SemaRef); - Sema::InstantiatingTemplate ConstrInst(SemaRef, - Req->getConstraintExpr()->getBeginLoc(), Req, Info, - Req->getConstraintExpr()->getSourceRange()); + + Sema::InstantiatingTemplate ConstrInst( + SemaRef, Constraint->getBeginLoc(), Req, + Sema::InstantiatingTemplate::ConstraintsCheck(), + Constraint->getSourceRange()); + if (ConstrInst.isInvalid()) return nullptr; - llvm::SmallVector<Expr *> Result; - if (!SemaRef.CheckConstraintSatisfaction( - nullptr, - AssociatedConstraint(Req->getConstraintExpr(), - SemaRef.ArgPackSubstIndex), - Result, TemplateArgs, Req->getConstraintExpr()->getSourceRange(), - Satisfaction) && - !Result.empty()) - TransConstraint = Result[0]; - assert(!Trap.hasErrorOccurred() && "Substitution failures must be handled " - "by CheckConstraintSatisfaction."); + + Sema::SFINAETrap Trap(SemaRef); + + Success = !SemaRef.CheckConstraintSatisfaction( + Req, AssociatedConstraint(Constraint, SemaRef.ArgPackSubstIndex), + TemplateArgs, Constraint->getSourceRange(), Satisfaction, + /*TopLevelConceptId=*/nullptr, &NewConstraint); + + assert(!Success || !Trap.hasErrorOccurred() && + "Substitution failures must be handled " + "by CheckConstraintSatisfaction."); } - ASTContext &C = SemaRef.Context; - if (TransConstraint.isUsable() && - TransConstraint.get()->isInstantiationDependent()) - return new (C) concepts::NestedRequirement(TransConstraint.get()); - if (TransConstraint.isInvalid() || !TransConstraint.get() || - Satisfaction.HasSubstitutionFailure()) { - SmallString<128> Entity; - llvm::raw_svector_ostream OS(Entity); - Req->getConstraintExpr()->printPretty(OS, nullptr, - SemaRef.getPrintingPolicy()); - return new (C) concepts::NestedRequirement( - SemaRef.Context, C.backupStr(Entity), Satisfaction); + + if (!Success || Satisfaction.HasSubstitutionFailure()) + return NestedReqWithDiag(Constraint, Satisfaction); + + // FIXME: const correctness + // MLTAL might be dependent. + if (!NewConstraint) { + if (!Satisfaction.IsSatisfied) + return NestedReqWithDiag(Constraint, Satisfaction); + + NewConstraint = Constraint; } - return new (C) - concepts::NestedRequirement(C, TransConstraint.get(), Satisfaction); + return new (C) concepts::NestedRequirement(C, NewConstraint, Satisfaction); } TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, @@ -3078,7 +3130,7 @@ bool Sema::SubstTypeConstraint( const ASTTemplateArgumentListInfo *TemplArgInfo = TC->getTemplateArgsAsWritten(); - if (!EvaluateConstraints) { + if (!EvaluateConstraints && !inParameterMappingSubstitution()) { UnsignedOrNone Index = TC->getArgPackSubstIndex(); if (!Index) Index = SemaRef.ArgPackSubstIndex; @@ -4378,6 +4430,16 @@ bool Sema::SubstTemplateArguments( return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out); } +bool Sema::SubstTemplateArgumentsInParameterMapping( + ArrayRef<TemplateArgumentLoc> 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 6967301..51b55b8 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3722,10 +3722,6 @@ public: 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, @@ -5110,9 +5106,13 @@ bool TreeTransform<Derived>::TransformTemplateArguments( typedef TemplateArgumentLocInventIterator<Derived, TemplateArgument::pack_iterator> PackLocIterator; + + TemplateArgumentListInfo *PackOutput = &Outputs; + TemplateArgumentListInfo New; + if (TransformTemplateArguments( PackLocIterator(*this, In.getArgument().pack_begin()), - PackLocIterator(*this, In.getArgument().pack_end()), Outputs, + PackLocIterator(*this, In.getArgument().pack_end()), *PackOutput, Uneval)) return true; @@ -5179,7 +5179,6 @@ bool TreeTransform<Derived>::TransformTemplateArguments( } return false; - } // FIXME: Find ways to reduce code duplication for pack expansions. @@ -6247,7 +6246,7 @@ ParmVarDecl *TreeTransform<Derived>::TransformFunctionTypeParam( /* DefArg */ nullptr); newParm->setScopeInfo(OldParm->getFunctionScopeDepth(), OldParm->getFunctionScopeIndex() + indexAdjustment); - transformedLocalDecl(OldParm, {newParm}); + getDerived().transformedLocalDecl(OldParm, {newParm}); return newParm; } @@ -7082,11 +7081,11 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType( TypeLocBuilder &TLB, UnaryTransformTypeLoc TL) { QualType Result = TL.getType(); + TypeSourceInfo *NewBaseTSI = TL.getUnderlyingTInfo(); if (Result->isDependentType()) { const UnaryTransformType *T = TL.getTypePtr(); - TypeSourceInfo *NewBaseTSI = - getDerived().TransformType(TL.getUnderlyingTInfo()); + NewBaseTSI = getDerived().TransformType(TL.getUnderlyingTInfo()); if (!NewBaseTSI) return QualType(); QualType NewBase = NewBaseTSI->getType(); @@ -7101,7 +7100,7 @@ QualType TreeTransform<Derived>::TransformUnaryTransformType( UnaryTransformTypeLoc NewTL = TLB.push<UnaryTransformTypeLoc>(Result); NewTL.setKWLoc(TL.getKWLoc()); NewTL.setParensRange(TL.getParensRange()); - NewTL.setUnderlyingTInfo(TL.getUnderlyingTInfo()); + NewTL.setUnderlyingTInfo(NewBaseTSI); return Result; } diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index cf32d4f..5456e73 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2424,7 +2424,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl( VisitDecl(D); llvm::SmallVector<TemplateArgument, 4> Args; for (unsigned I = 0; I < D->NumTemplateArgs; ++I) - Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/true)); + Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false)); D->setTemplateArguments(Args); } diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 70b898a..eef97a8 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -807,15 +807,19 @@ readConstraintSatisfaction(ASTRecordReader &Record) { if (!Satisfaction.IsSatisfied) { unsigned NumDetailRecords = Record.readInt(); for (unsigned i = 0; i != NumDetailRecords; ++i) { - if (/* IsDiagnostic */Record.readInt()) { + auto Kind = Record.readInt(); + if (Kind == 0) { SourceLocation DiagLocation = Record.readSourceLocation(); StringRef DiagMessage = C.backupStr(Record.readString()); - Satisfaction.Details.emplace_back( - new (C) ConstraintSatisfaction::SubstitutionDiagnostic( - DiagLocation, DiagMessage)); - } else + Satisfaction.Details.emplace_back(new ( + C) ConstraintSubstitutionDiagnostic(DiagLocation, DiagMessage)); + } else if (Kind == 1) { Satisfaction.Details.emplace_back(Record.readExpr()); + } else { + assert(Kind == 2); + Satisfaction.Details.emplace_back(Record.readConceptReference()); + } } } return Satisfaction; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index ebda91e..acf3453 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -482,14 +482,20 @@ addConstraintSatisfaction(ASTRecordWriter &Record, if (!Satisfaction.IsSatisfied) { Record.push_back(Satisfaction.NumRecords); for (const auto &DetailRecord : Satisfaction) { - auto *E = dyn_cast<Expr *>(DetailRecord); - Record.push_back(/* IsDiagnostic */ E == nullptr); - if (E) - Record.AddStmt(E); - else { - auto *Diag = cast<std::pair<SourceLocation, StringRef> *>(DetailRecord); + if (auto *Diag = dyn_cast<const ConstraintSubstitutionDiagnostic *>( + DetailRecord)) { + Record.push_back(/*Kind=*/0); Record.AddSourceLocation(Diag->first); Record.AddString(Diag->second); + continue; + } + if (auto *E = dyn_cast<const Expr *>(DetailRecord)) { + Record.push_back(/*Kind=*/1); + Record.AddStmt(const_cast<Expr *>(E)); + } else { + Record.push_back(/*Kind=*/2); + auto *CR = cast<const ConceptReference *>(DetailRecord); + Record.AddConceptReference(CR); } } } diff --git a/clang/test/AST/ast-dump-concepts.cpp b/clang/test/AST/ast-dump-concepts.cpp index 84d981d..9419dba 100644 --- a/clang/test/AST/ast-dump-concepts.cpp +++ b/clang/test/AST/ast-dump-concepts.cpp @@ -20,8 +20,9 @@ struct Foo { // CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'binary_concept' // CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13, col:31> 'bool' Concept {{.*}} 'binary_concept' // CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:13:9> col:9 - // CHECK-NEXT: | |-TemplateArgument type 'type-parameter-1-0' - // CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0 + // CHECK-NEXT: | |-TemplateArgument type 'R' + // CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0 + // CHECK-NEXT: | | `-TemplateTypeParm {{.*}} 'R' // CHECK-NEXT: | `-TemplateArgument type 'int' // CHECK-NEXT: | `-BuiltinType {{.*}} 'int' // CHECK-NEXT: |-TemplateArgument {{.*}} type 'R' @@ -35,8 +36,9 @@ struct Foo { // CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'unary_concept' // CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13> 'bool' // CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:10:9> col:9 - // CHECK-NEXT: | `-TemplateArgument type 'type-parameter-1-0' - // CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0 + // CHECK-NEXT: | `-TemplateArgument type 'R' + // CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0 + // CHECK-NEXT: | `-TemplateTypeParm {{.*}} 'R' template <unary_concept R> Foo(R); diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp index 781fb9f..9a3adbc 100644 --- a/clang/test/AST/ast-dump-ctad-alias.cpp +++ b/clang/test/AST/ast-dump-ctad-alias.cpp @@ -185,17 +185,18 @@ void foo() { // CHECK-NEXT: | |-BinaryOperator {{.*}} 'bool' '&&' // CHECK-NEXT: | | |-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'invocable' // CHECK-NEXT: | | | |-ImplicitConceptSpecializationDecl {{.*}} -// CHECK-NEXT: | | | | |-TemplateArgument type 'type-parameter-0-2' -// CHECK-NEXT: | | | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-2' dependent depth 0 index 2 -// CHECK-NEXT: | | | | `-TemplateArgument pack '<GH124715::Packs<type-parameter-0-1...>>' -// CHECK-NEXT: | | | | `-TemplateArgument type 'GH124715::Packs<type-parameter-0-1...>' -// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'GH124715::Packs<type-parameter-0-1...>' dependent -// CHECK-NEXT: | | | | |-name: 'GH124715::Packs' +// CHECK-NEXT: | | | | |-TemplateArgument type 'U' +// CHECK-NEXT: | | | | | `-TemplateTypeParmType {{.*}} 'U' dependent depth 0 index 2 +// CHECK-NEXT: | | | | | `-TemplateTypeParm {{.*}} 'U' +// CHECK-NEXT: | | | | `-TemplateArgument pack '<Packs<Ts...>>' +// CHECK-NEXT: | | | | `-TemplateArgument type 'Packs<Ts...>' +// CHECK-NEXT: | | | | `-TemplateSpecializationType {{.*}} 'Packs<Ts...>' dependent +// CHECK-NEXT: | | | | |-name: 'Packs':'GH124715::Packs' qualified // CHECK-NEXT: | | | | | `-ClassTemplateDecl {{.*}} Packs -// CHECK-NEXT: | | | | `-TemplateArgument pack '<type-parameter-0-1...>' -// CHECK-NEXT: | | | | `-TemplateArgument type 'type-parameter-0-1...' -// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'type-parameter-0-1...' dependent -// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent contains_unexpanded_pack depth 0 index 1 pack +// CHECK-NEXT: | | | | `-TemplateArgument type 'Ts...' +// CHECK-NEXT: | | | | `-PackExpansionType {{.*}} 'Ts...' dependent +// CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'Ts' dependent contains_unexpanded_pack depth 0 index 1 pack +// CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'Ts' // CHECK-NEXT: | | | |-TemplateArgument {{.*}} type 'U':'type-parameter-0-2' // CHECK-NEXT: | | | | `-TemplateTypeParmType {{.*}} 'U' dependent depth 0 index 2 // CHECK-NEXT: | | | | `-TemplateTypeParm {{.*}} 'U' diff --git a/clang/test/CXX/drs/cwg25xx.cpp b/clang/test/CXX/drs/cwg25xx.cpp index 5c2948f..0e0fc73 100644 --- a/clang/test/CXX/drs/cwg25xx.cpp +++ b/clang/test/CXX/drs/cwg25xx.cpp @@ -243,19 +243,20 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07 // since-cxx20-note@#cwg2565-VC {{because 'b' would be invalid: argument may not have 'void' type}} template<typename T> - concept ErrorRequires = requires (ErrorRequires auto x) { + concept ErrorRequires = requires (ErrorRequires auto x) { // #cwg2565-expr // since-cxx20-error@-1 {{a concept definition cannot refer to itself}} // since-cxx20-note@-2 {{declared here}} // since-cxx20-error@-3 {{'auto' not allowed in requires expression parameter}} x; }; static_assert(ErrorRequires<int>); - // since-cxx20-error@-1 {{static assertion failed}} - // since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // since-cxx20-error@-1 {{static assertion failed}} \ + // since-cxx20-note@-1 {{because 'int' does not satisfy 'ErrorRequires'}} \ + // since-cxx20-note@#cwg2565-expr {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} template<typename T> concept NestedErrorInRequires = requires (T x) { // #cwg2565-NEIR - requires requires (NestedErrorInRequires auto y) { + requires requires (NestedErrorInRequires auto y) { // #cwg2565-NEIR-inner // since-cxx20-error@-1 {{a concept definition cannot refer to itself}} // since-cxx20-note@#cwg2565-NEIR {{declared here}} // since-cxx20-error@-3 {{'auto' not allowed in requires expression parameter}} @@ -263,8 +264,9 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07 }; }; static_assert(NestedErrorInRequires<int>); - // since-cxx20-error@-1 {{static assertion failed}} - // since-cxx20-note@-2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // since-cxx20-error@-1 {{static assertion failed}} \ + // since-cxx20-note@-1 {{because 'int' does not satisfy 'NestedErrorInRequires'}} \ + // since-cxx20-note-re@#cwg2565-NEIR-inner {{because {{.*}} would be invalid: constraint depends on a previously diagnosed expression}} #endif } // namespace cwg2565 diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp index 28b5d0a..af2fc93 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp @@ -140,7 +140,8 @@ concept C7 = sizeof(T) == 1 || sizeof( ::type) == 1; static_assert(!C6<short>); -static_assert(!C6<char>); // expected-note{{while checking the satisfaction of concept 'C6<char>' requested here}} +static_assert(!C6<char>); +// expected-note@-1 {{while checking the satisfaction of concept 'C6<char>' requested here}} static_assert(C7<char>); static_assert(!C7<short>); // expected-note{{while checking the satisfaction of concept 'C7<short>' requested here}} diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp index 31587a9..af2dce8 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp @@ -35,14 +35,14 @@ using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class templ using r2i3 = r2<D>; using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}} -template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}} +template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}} struct r3 {}; using r3i1 = r3<int>; using r3i2 = r3<A>; using r3i3 = r3<A &>; using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}} -using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}} +using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}} // Non-dependent expressions @@ -89,7 +89,7 @@ template<typename T> concept Large = sizeof(typename remove_reference<T>::type) >= 4; // expected-note@-1{{because 'sizeof(typename remove_reference<short &>::type) >= 4' (2 >= 4) evaluated to false}} -template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large':}} +template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'short &' does not satisfy 'Large'}} struct r7 {}; using r7i1 = r7<int>; @@ -149,7 +149,7 @@ namespace std_example { template<typename T> constexpr bool is_same_v<T, T> = true; template<typename T, typename U> concept same_as = is_same_v<T, U>; - // expected-note@-1 {{because 'is_same_v<int, int *>' evaluated to false}} + // expected-note@-1 {{because 'is_same_v<int, typename std_example::T2::inner>' evaluated to false}} static_assert(C1<int>); static_assert(C1<int*>); @@ -160,7 +160,7 @@ namespace std_example { template<typename T> concept C2 = requires(T x) { {*x} -> same_as<typename T::inner>; - // expected-note@-1{{because type constraint 'same_as<int, typename std_example::T2::inner>' was not satisfied:}} + // expected-note@-1{{because 'same_as<int, typename std_example::T2::inner>' evaluated to false}} // expected-note@-2{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}} }; @@ -173,9 +173,9 @@ namespace std_example { int operator *() { return 0; } }; static_assert(C2<T1>); - template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}} + template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'T2' does not satisfy 'C2'}} using c2c1 = C2_check<int>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}} - using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}} + using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = T2]}} template<typename T> void g(T t) noexcept(sizeof(T) == 1) {} diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp index 033ae34..70a96be 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp @@ -43,11 +43,10 @@ namespace std_example { requires sizeof(a) == 4; // OK requires a == 0; // expected-error{{substitution into constraint expression resulted in a non-constant expression}} // expected-note@-1{{while checking the satisfaction of nested requirement requested here}} - // expected-note@-2{{in instantiation of requirement here}} - // expected-note@-3{{while checking the satisfaction of nested requirement requested here}} - // expected-note@-6{{while substituting template arguments into constraint expression here}} - // expected-note@-5{{function parameter 'a' with unknown value cannot be used in a constant expression}} - // expected-note@-8{{declared here}} + // expected-note@-2{{while checking the satisfaction of nested requirement requested here}} + // expected-note@-5{{while substituting template arguments into constraint expression here}} + // expected-note@-4{{function parameter 'a' with unknown value cannot be used in a constant expression}} + // expected-note@-7{{declared here}} }; static_assert(C2<int>); // expected-error{{static assertion failed}} // expected-note@-1{{while checking the satisfaction of concept 'C2<int>' requested here}} @@ -84,31 +83,26 @@ static_assert(Pipes<S>); static_assert(Pipes<double>); static_assert(Amps1<S>); -static_assert(!Amps1<double>); +static_assert(Amps1<double>); static_assert(Amps2<S>); -static_assert(!Amps2<double>); +static_assert(Amps2<double>); template<class T> -void foo1() requires requires (T x) { // #foo1 +void foo1() requires requires (T x) { requires - True<decltype(x.value)> // #foo1Value + True<decltype(x.value)> && True<T>; } {} template<class T> void fooPipes() requires Pipes<T> {} -template<class T> void fooAmps1() requires Amps1<T> {} // #fooAmps1 +template<class T> void fooAmps1() requires Amps1<T> {} void foo() { foo1<S>(); - foo1<int>(); // expected-error {{no matching function for call to 'foo1'}} - // expected-note@#foo1Value {{because 'True<decltype(x.value)> && True<T>' would be invalid: member reference base type 'int' is not a structure or union}} - // expected-note@#foo1 {{candidate template ignored: constraints not satisfied [with T = int]}} + foo1<int>(); fooPipes<S>(); fooPipes<int>(); fooAmps1<S>(); - fooAmps1<int>(); // 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<decltype(x.value)> && True<T> && !False<T>' would be invalid: member reference base type 'int' is not a structure or union}} + fooAmps1<int>(); } template<class T> @@ -158,15 +152,16 @@ void func() { // expected-note@#bar {{while substituting template arguments into constraint expression here}} // expected-note@#bar {{while checking the satisfaction of nested requirement requested here}} // expected-note@#bar {{candidate template ignored: constraints not satisfied [with T = False]}} - // expected-note@#bar {{because 'X<SubstitutionFailureNestedRequires::ErrorExpressions_NotSF::False>::value' evaluated to false}} + // expected-note@#bar {{because 'X<False>::value' evaluated to false}} bar<int>(); + // expected-error@-1 {{no matching function for call to 'bar'}} \ // expected-note@-1 {{while checking constraint satisfaction for template 'bar<int>' 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 5199708..5dcb188 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp @@ -39,14 +39,14 @@ using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template<typename T> requires requires { sizeof(T); } // expected-note@-1{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} -// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}} +// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}} struct r3 {}; using r3i1 = r3<int>; using r3i2 = r3<A>; using r3i3 = r3<A &>; using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}} -using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}} +using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}} template<typename T> requires requires (T t) { 0; "a"; (void)'a'; } struct r4 {}; diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp index 5433cfb..28dff33 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp @@ -182,14 +182,14 @@ namespace std_example { static_assert(C1<has_inner_and_type> && C2<has_inner_and_type> && C3<has_inner_and_type>); template<C1 T> struct C1_check {}; // expected-note@-1 {{because 'int' does not satisfy 'C1'}} - // expected-note@-2 {{because 'std_example::has_type' does not satisfy 'C1'}} + // expected-note@-2 {{because 'has_type' does not satisfy 'C1'}} template<C2 T> struct C2_check {}; - // expected-note@-1 {{because 'std_example::has_inner' does not satisfy 'C2'}} + // expected-note@-1 {{because 'has_inner' does not satisfy 'C2'}} template<C3 T> struct C3_check {}; // expected-note@-1 {{because 'void' does not satisfy 'C3'}} using c1 = C1_check<int>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}} - using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}} - using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}} + using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = has_type]}} + using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = has_inner]}} using c4 = C3_check<void>; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}} } @@ -199,10 +199,10 @@ template <typename T> concept C = requires { requires requires { T::a; }; }; // expected-note@-1 {{because 'T::a' would be invalid: no member named 'a' in 'PR48656::T1'}} template <C...> struct A {}; -// expected-note@-1 {{because 'PR48656::T1' does not satisfy 'C'}} +// expected-note@-1 {{because 'T1' does not satisfy 'C'}} struct T1 {}; -template struct A<T1>; // expected-error {{constraints not satisfied for class template 'A' [with $0 = <PR48656::T1>]}} +template struct A<T1>; // expected-error {{constraints not satisfied for class template 'A' [with $0 = <T1>]}} struct T2 { static constexpr bool a = false; }; template struct A<T2>; diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp index 59e6a48..6dea0c6 100644 --- a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp @@ -28,9 +28,8 @@ template<typename T> requires requires { requires S<T>{}; // expected-error@-1{{atomic constraint must be of type 'bool' (found 'S<int>')}} // expected-note@-2{{while checking the satisfaction}} - // expected-note@-3{{in instantiation of requirement}} - // expected-note@-4{{while checking the satisfaction}} - // expected-note@-6{{while substituting template arguments}} + // expected-note@-3{{while checking the satisfaction of nested requirement}} + // expected-note@-5{{while substituting template arguments}} // expected-note@#F3INST{{while checking constraint satisfaction}} // expected-note@#F3INST{{while substituting deduced template arguments into function template 'f3' [with T = int]}} // diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp index 3992835..34c5c5d 100644 --- a/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp @@ -1,21 +1,31 @@ // RUN: %clang_cc1 -std=c++2a -x c++ -verify %s +// RUN: %clang_cc1 -std=c++2c -x c++ -verify %s template<typename T> concept True = true; -template<typename T> concept Foo = True<T*>; -template<typename T> concept Bar = Foo<T&>; -template<typename T> requires Bar<T> struct S { }; -template<typename T> requires Bar<T> && true struct S<T> { }; +template<typename T> concept Foo = True<T*>; // #Foo +template<typename T> concept Bar = Foo<T&>; // #Bar +template<typename T> requires Bar<T> struct S { }; // #S +template<typename T> requires Bar<T> && true struct S<T> { }; // #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<typename T> concept True2 = sizeof(T) >= 0; -template<typename T> concept Foo2 = True2<T*>; -// expected-error@-1{{'type name' declared as a pointer to a reference of type 'type-parameter-0-0 &'}} -template<typename T> concept Bar2 = Foo2<T&>; -// expected-note@-1{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}} -template<typename T> requires Bar2<T> struct S2 { }; +template<typename T> concept Foo2 = True2<T*>; // #Foo2 + +template<typename T> concept Bar2 = Foo2<T&>; // #Bar2 +// expected-note@-1 3{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}} +template<typename T> requires Bar2<T> struct S2 { }; // #SpecS2_1 // expected-note@-1{{template is declared here}} -template<typename T> requires Bar2<T> && true struct S2<T> { }; +template<typename T> requires Bar2<T> && true struct S2<T> { }; // #SpecS2_2 // expected-error@-1{{class template partial specialization is not more specialized than the primary template}} -// expected-note@-2{{while calculating associated constraint of template 'S2<T>' here}} +// expected-error@#Foo2{{'type name' declared as a pointer to a reference of type 'T &'}} + namespace type_pack { template<typename... Args> @@ -71,16 +81,31 @@ namespace non_type_pack { namespace PR47174 { // This checks that we don't crash with a failed substitution on the first constrained argument when // performing normalization. -template <Bar2 T, True U> +template <Bar2 T, True U> // #S3_Header requires true struct S3; // expected-note {{template is declared here}} template <True T, True U> -requires true struct S3<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}} +requires true struct S3<T, U>; +// expected-error@-1 {{class template partial specialization is not more specialized than the primary template}} +// expected-error@#Foo2 2{{'type name' declared as a pointer to a reference of type 'T &'}} +// expected-note@#SpecS2_1 {{while substituting into concept arguments here}} +// expected-note@#SpecS2_2 {{while substituting into concept arguments here}} +// expected-note@#S3_Header {{while substituting into concept arguments here}} +// expected-note@#Bar2 {{while substituting into concept arguments here}} + // Same as above, for the second position (but this was already working). -template <True T, Bar2 U> -requires true struct S4; // expected-note {{template is declared here}} +template <True T, Bar2 U> // #S4_Header +requires true struct S4; // #S4 template <True T, True U> -requires true struct S4<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}} +requires true struct S4<T, U>; // #S4-spec +// expected-error@-1 {{class template partial specialization is not more specialized than the primary template}} +// expected-error@#Foo2 {{'type name' declared as a pointer to a reference of type 'U &'}} +// expected-note@#S4_Header {{while substituting into concept arguments here}} +// expected-note@#S4 {{template is declared here}} +// expected-note@#S4 {{similar constraint expressions not considered equivalent}} +// expected-note@#S4-spec {{similar constraint expression here}} + + struct X { template<int> struct Y { @@ -96,7 +121,7 @@ template<class T> requires C1<T> && C2<T> void t1() = delete; // expected-note { template void t1<X>(); void t1() { t1<X>(); } // expected-error {{call to deleted function 't1'}} -template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}} +template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}} template<class T> requires C2<T> void t2() {}; // expected-note 2 {{candidate function}} template void t2<X>(); // expected-error {{partial ordering for explicit instantiation of 't2' is ambiguous}} void t2() { t2<X>(); } // expected-error {{call to 't2' is ambiguous}} diff --git a/clang/test/CXX/temp/temp.param/p10-2a.cpp b/clang/test/CXX/temp/temp.param/p10-2a.cpp index 4f5fdd3..c0406f8 100644 --- a/clang/test/CXX/temp/temp.param/p10-2a.cpp +++ b/clang/test/CXX/temp/temp.param/p10-2a.cpp @@ -86,16 +86,18 @@ using f1 = F<int>; using f2 = F<long>; // expected-error {{constraints not satisfied for alias template 'F' [with T = long]}} template<typename T, typename... Ts> -concept OneOf = (is_same_v<T, Ts> || ...); -// expected-note@-1 2{{because 'is_same_v<char, char[1]>' evaluated to false}} -// expected-note@-2 2{{and 'is_same_v<char, char[2]>' evaluated to false}} -// expected-note@-3 {{because 'is_same_v<short, int>' evaluated to false}} -// expected-note@-4 {{and 'is_same_v<short, long>' evaluated to false}} -// expected-note@-5 {{and 'is_same_v<short, char>' evaluated to false}} -// expected-note@-6 3{{because 'is_same_v<int, char[1]>' evaluated to false}} -// expected-note@-7 3{{and 'is_same_v<int, char[2]>' evaluated to false}} -// expected-note@-8 2{{because 'is_same_v<std::nullptr_t, char>' evaluated to false}} -// expected-note@-9 2{{and 'is_same_v<std::nullptr_t, int>' evaluated to false}} +concept OneOf = (is_same_v<T, Ts> || ...); // #OneOf +// expected-note@#OneOf 2{{because 'is_same_v<char, char[1]>' evaluated to false}} +// expected-note@#OneOf 2{{and 'is_same_v<char, char[2]>' evaluated to false}} +// expected-note@#OneOf {{because 'is_same_v<short, int>' evaluated to false}} +// expected-note@#OneOf {{and 'is_same_v<short, long>' evaluated to false}} +// expected-note@#OneOf {{and 'is_same_v<short, char>' evaluated to false}} +// expected-note@#OneOf 3{{because 'is_same_v<int, char[1]>' evaluated to false}} +// expected-note@#OneOf 3{{and 'is_same_v<int, char[2]>' evaluated to false}} +// expected-note@#OneOf {{because 'is_same_v<decltype(nullptr), char>' evaluated to false}} +// expected-note@#OneOf {{because 'is_same_v<std::nullptr_t, char>' evaluated to false}} +// expected-note@#OneOf {{and 'is_same_v<std::nullptr_t, int>' evaluated to false}} +// expected-note@#OneOf {{and 'is_same_v<decltype(nullptr), int>' evaluated to false}} template<OneOf<char[1], char[2]> T, OneOf<int, long, char> U> // expected-note@-1 2{{because 'OneOf<char, char[1], char[2]>' evaluated to false}} @@ -124,6 +126,7 @@ using I = int; using i1 = I<1>; using i2 = I<'a'>; +// FIXME: This crashes with -std=c++2c using i3 = I<nullptr>; // expected-error@-1 {{constraints not satisfied for alias template 'I' [with x = nullptr]}} diff --git a/clang/test/CodeGen/X86/avx-cxx-record.cpp b/clang/test/CodeGen/X86/avx-cxx-record.cpp index 6ce6815..b20bcdd 100644 --- a/clang/test/CodeGen/X86/avx-cxx-record.cpp +++ b/clang/test/CodeGen/X86/avx-cxx-record.cpp @@ -1,7 +1,9 @@ // RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -emit-llvm -O2 -target-cpu x86-64-v3 -o - | FileCheck %s // RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -emit-llvm -O2 -target-cpu x86-64-v3 -fclang-abi-compat=20 -o - | FileCheck --check-prefix CLANG-20 %s +// RUN: %clang_cc1 %s -triple x86_64-sie-ps4 -emit-llvm -O2 -target-cpu x86-64-v3 -o - | FileCheck --check-prefix CLANG-20 %s using UInt64x2 = unsigned long long __attribute__((__vector_size__(16), may_alias)); +using UInt64x4 = unsigned long long __attribute__((__vector_size__(32), may_alias)); template<int id> struct XMM1 { @@ -23,3 +25,24 @@ XMM2 foo() { ((XMM1<1>*)&result)->x = UInt64x2{3, 4}; return result; } + +template<int id> +struct YMM1 { + UInt64x4 x; +}; + +struct YMM2 : YMM1<0>, YMM1<1> { +}; + +// CHECK: define{{.*}} @_Z3barv({{.*}} [[ARG:%.*]]){{.*}} +// CLANG-20: define{{.*}} <8 x double> @_Z3barv() +// CHECK: entry: +// CHECK-NEXT: store {{.*}}, ptr [[ARG]]{{.*}} +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr {{.*}}, ptr [[ARG]]{{.*}} +// CHECK-NEXT: store {{.*}}, ptr [[TMP1]]{{.*}} +YMM2 bar() { + YMM2 result; + ((YMM1<0>*)&result)->x = UInt64x4{1, 2, 3, 4}; + ((YMM1<1>*)&result)->x = UInt64x4{5, 6, 7, 8}; + return result; +} diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp index fd1a5c0..404b928 100644 --- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp +++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp @@ -587,6 +587,23 @@ static_assert(__is_same(decltype(a), A<A<int>>)); } // namespace GH133132 +namespace GH131408 { + +struct Node {}; + +template <class T, Node> +struct A { + A(T) {} +}; + +template <class T> +using AA = A<T, {}>; + +AA a{0}; + +static_assert(__is_same(decltype(a), A<int, Node{}>)); +} + namespace GH130604 { template <typename T> struct A { A(T); diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp index 99a82d9..ce86266 100644 --- a/clang/test/SemaCXX/cxx23-assume.cpp +++ b/clang/test/SemaCXX/cxx23-assume.cpp @@ -127,13 +127,12 @@ struct F { template <typename T> constexpr int f5() requires C<T> { return 1; } // expected-note {{while checking the satisfaction}} - // expected-note@-1 {{while substituting template arguments}} - // expected-note@-2 {{candidate template ignored}} + // expected-note@-1 {{candidate template ignored}} template <typename T> -constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}} - // expected-note@-1 4 {{while substituting template arguments}} - // expected-note@-2 {{candidate template ignored}} +constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}} \ + // expected-note 4 {{while substituting template arguments}} \ + // expected-note {{candidate template ignored}} static_assert(f5<int>() == 1); static_assert(f5<D>() == 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 74b3573..6777dc2 100644 --- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp +++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp @@ -1257,13 +1257,13 @@ void f() { (&A::e)(a, a); // expected-error@-1 {{no matching function for call to 'e'}} \ // expected-note@#tpl-address-e{{candidate template ignored: constraints not satisfied [with T = A, U = A]}} \ - // expected-note@#tpl-address-e{{because '__is_same(tpl_address::A, int)' evaluated to false}} + // expected-note@#tpl-address-e{{because '__is_same(A, int)' evaluated to false}} (&A::e<A>)(a, 0); (&A::e<A>)(a, a); // expected-error@-1 {{no matching function for call to 'e'}} \ // expected-note@#tpl-address-e{{candidate template ignored: constraints not satisfied [with T = A, U = A]}} \ - // expected-note@#tpl-address-e{{because '__is_same(tpl_address::A, int)' evaluated to false}} + // expected-note@#tpl-address-e{{because '__is_same(A, int)' evaluated to false}} (&A::e<A, int>)(a, 0); @@ -1273,12 +1273,12 @@ void f() { (&A::f<A>)(a); // expected-error@-1 {{no matching function for call to 'f'}} \ // expected-note@#tpl-address-f{{candidate template ignored: constraints not satisfied [with T = A]}} \ - // expected-note@#tpl-address-f{{because '__is_same(tpl_address::A, int)' evaluated to false}} + // expected-note@#tpl-address-f{{because '__is_same(A, int)' evaluated to false}} (&A::f)(a); // expected-error@-1 {{no matching function for call to 'f'}} \ // expected-note@#tpl-address-f{{candidate template ignored: constraints not satisfied [with T = A]}} \ - // expected-note@#tpl-address-f{{because '__is_same(tpl_address::A, int)' evaluated to false}} + // expected-note@#tpl-address-f{{because '__is_same(A, int)' evaluated to false}} (&A::g)(a); (&A::g)(a, 0); diff --git a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp index 4220486..137f46e 100644 --- a/clang/test/SemaCXX/cxx2c-fold-exprs.cpp +++ b/clang/test/SemaCXX/cxx2c-fold-exprs.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++2c -verify %s -template <class T> concept A = true; -template <class T> concept C = A<T> && true; +template <class T> concept A = (T(), true); +template <class T> concept C = A<T> && true; // #C template <class T> concept D = A<T> && __is_same(T, int); @@ -40,13 +40,23 @@ constexpr int i(T...) { return 1; }; // expected-note {{candidate}} static_assert(i(0) == 1); // expected-error {{call to 'i' is ambiguous}} -template <class... T> requires (A<T> || ... || true) -constexpr int j(T...) { return 0; }; -template <class... T> requires (C<T> && ... && true) -constexpr int j(T...) { return 1; }; +template <class... T> requires (A<T> || ... || true) constexpr int j(T...) { return 0; }; // #j1 +template <class... T> requires (C<T> && ... && true) constexpr int j(T...) { return 1; }; // #j2 static_assert(j(0) == 1); +// expected-error@-1 {{call to 'j' is ambiguous}} +// expected-note@#j1 {{candidate function [with T = <int>]}} +// expected-note@#j2 {{candidate function [with T = <int>]}} +// expected-note@#j2 {{imilar constraint expressions not considered equivalent}} +// expected-note@#j1 {{similar constraint expression here}} + + static_assert(j() == 1); +// expected-error@-1 {{call to 'j' is ambiguous}} +// expected-note@#j1 {{candidate function [with T = <>]}} +// expected-note@#j2 {{candidate function [with T = <>]}} +// expected-note@#j2 {{imilar constraint expressions not considered equivalent}} +// expected-note@#j1 {{similar constraint expression here}} @@ -107,7 +117,7 @@ void test() { } namespace substitution { - struct S { +struct S { using type = int; }; @@ -144,51 +154,69 @@ consteval int Or3() requires (C<typename T::type> || ... || C<typename U::type>) static_assert(And1<>() == 1); static_assert(And1<S>() == 1); static_assert(And1<S, S>() == 1); +// FIXME: The diagnostics are not so great static_assert(And1<int>() == 1); // expected-error {{no matching function for call to 'And1'}} - // expected-note@#and1 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and1 {{because substituted constraint expression is ill-formed}} + // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int>]}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And1<S, int>() == 1); // expected-error {{no matching function for call to 'And1'}} - // expected-note@#and1 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and1 {{because substituted constraint expression is ill-formed}} + // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <S, int>]}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And1<int, S>() == 1); // expected-error {{no matching function for call to 'And1'}} - // expected-note@#and1 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and1 {{because substituted constraint expression is ill-formed}} + // expected-note@#and1 {{candidate template ignored: constraints not satisfied [with T = <int, S>]}} + // expected-note@#and1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And2<S>() == 2); static_assert(And2<S, S>() == 2); -static_assert(And2<int>() == 2); +static_assert(And2<int>() == 2); // expected-error {{no matching function for call to 'And2'}} + // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}} + // expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + static_assert(And2<int, int>() == 2); // expected-error {{no matching function for call to 'And2'}} - // expected-note@#and2 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and2 {{because substituted constraint expression is ill-formed}} + // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}} \ + // expected-note@#and2 {{because 'typename U::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And2<S, int>() == 2); // expected-error {{no matching function for call to 'And2'}} - // expected-note@#and2 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and2 {{because substituted constraint expression is ill-formed}} + // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}} + // expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And2<int, S>() == 2); // expected-error {{no matching function for call to 'And2'}} - // expected-note@#and2 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and2 {{because substituted constraint expression is ill-formed}} + // expected-note@#and2 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}} + // expected-note@#and2 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(And3<S>() == 3); static_assert(And3<S, S>() == 3); static_assert(And3<int>() == 3); // expected-error {{no matching function for call to 'And3'}} - // expected-note@#and3 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and3 {{because substituted constraint expression is ill-formed}} + // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}} + // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + static_assert(And3<int, int>() == 3); // expected-error {{no matching function for call to 'And3'}} - // expected-note@#and3 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and3 {{because substituted constraint expression is ill-formed}} + // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <int>]}} + // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + static_assert(And3<S, int>() == 3); // expected-error {{no matching function for call to 'And3'}} - // expected-note@#and3 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and3 {{because substituted constraint expression is ill-formed}} + // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = S, U = <int>]}} + // expected-note@#and3 {{because 'typename U::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} + static_assert(And3<int, S>() == 3); // expected-error {{no matching function for call to 'And3'}} - // expected-note@#and3 {{candidate template ignored: constraints not satisfied}} - // expected-note@#and3 {{because substituted constraint expression is ill-formed}} + // expected-note@#and3 {{candidate template ignored: constraints not satisfied [with T = int, U = <S>]}} + // expected-note@#and3 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(Or1<>() == 1); // expected-error {{no matching function for call to 'Or1'}} @@ -198,25 +226,26 @@ static_assert(Or1<int, S>() == 1); static_assert(Or1<S, int>() == 1); static_assert(Or1<S, S>() == 1); static_assert(Or1<int>() == 1); // expected-error {{no matching function for call to 'Or1'}} - // expected-note@#or1 {{candidate template ignored: constraints not satisfied}} \ - // expected-note@#or1 {{because substituted constraint expression is ill-formed}} - + // expected-note@#or1 {{candidate template ignored: constraints not satisfied}} + // expected-note@#or1 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(Or2<S>() == 2); static_assert(Or2<int, S>() == 2); static_assert(Or2<S, int>() == 2); static_assert(Or2<S, S>() == 2); static_assert(Or2<int>() == 2); // expected-error {{no matching function for call to 'Or2'}} - // expected-note@#or2 {{candidate template ignored: constraints not satisfied}} \ - // expected-note@#or2 {{because substituted constraint expression is ill-formed}} - + // expected-note@#or2 {{candidate template ignored: constraints not satisfied [with T = int, U = <>]}} + // expected-note@#or2 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} static_assert(Or3<S>() == 3); static_assert(Or3<int, S>() == 3); static_assert(Or3<S, int>() == 3); static_assert(Or3<S, S>() == 3); static_assert(Or3<int>() == 3); // expected-error {{no matching function for call to 'Or3'}} - // expected-note@#or3 {{candidate template ignored: constraints not satisfied}} \ - // expected-note@#or3 {{because substituted constraint expression is ill-formed}} + // expected-note@#or3 {{candidate template ignored: constraints not satisfied}} + // expected-note@#or3 {{because 'typename T::type' does not satisfy 'C'}} + // expected-note@#C {{because 'T' does not satisfy 'A'}} } namespace bool_conversion_break { @@ -226,7 +255,7 @@ struct Thingy { static constexpr int compare(const Thingy&) {return 1;} }; template <typename ...T, typename ...U> -void f(A<T ...> *, A<U ...> *) // expected-note {{candidate template ignored: failed template argument deduction}} +void f(A<T ...> *, A<U ...> *) // expected-note {{candidate template ignored: constraints not satisfied}} requires (T::compare(U{}) && ...); // expected-error {{atomic constraint must be of type 'bool' (found 'int')}} void g() { @@ -269,9 +298,7 @@ struct S { static_assert(S<int>::f<int>() == 2); -static_assert(S<int>::g<int>() == 2); // expected-error {{call to 'g' is ambiguous}} - // expected-note@#nested-ambiguous-g1 {{candidate}} - // expected-note@#nested-ambiguous-g2 {{candidate}} +static_assert(S<int>::g<int>() == 2); } @@ -384,3 +411,98 @@ struct LazyLitMatrix<index_by<Indices...>, init> { } } + +namespace GH135190 { +template <typename T> +concept A = __is_same_as(T, int) || __is_same_as(T, double) ; + +template <typename T> +concept B = A<T> && __is_same_as(T, double); + +template <class... Ts> +requires(A<Ts> && ...) +constexpr int g() { + return 1; +} + +template <class... Ts> +requires(B<Ts> && ...) +constexpr int g() { + return 2; +} + +static_assert(g<double>() == 2); + + +template <class... Ts> +concept all_A = (A<Ts> && ...); + +template <class... Ts> +concept all_B = (B<Ts> && ...); + +template <class... Ts> +requires all_A<Ts...> +constexpr int h() { + return 1; +} + +template <class... Ts> +requires all_B<Ts...> +constexpr int h() { + return 2; +} + +static_assert(h<double>() == 2); +} + + +namespace parameter_mapping_regressions { + +namespace case1 { +namespace std { +template <class _Tp, class... _Args> +constexpr bool is_constructible_v = __is_constructible(_Tp, _Args...); +template <class _Tp, class... _Args> +concept constructible_from = is_constructible_v<_Tp, _Args...>; +template <class _Tp> +concept default_initializable = true; +template <class> using iterator_t = int; +template <class _Tp> +concept view = constructible_from<_Tp, _Tp>; +template <class... _Views> + requires(view<_Views> && ...) +class zip_transform_view; +} // namespace std +struct IterDefaultCtrView {}; +template <class... Views> +using Iter = std::iterator_t<std::zip_transform_view<Views...>>; +static_assert( + std::default_initializable<Iter<IterDefaultCtrView, IterDefaultCtrView>>); + +} + +namespace case2 { + +template <class _Bp> +constexpr bool False = false; + +template <class... _Views> +concept __zip_all_random_access = (False<_Views> && ...); +// expected-note@-1 {{evaluated to false}} + +template <typename... _Views> +struct zip_view { + void f() requires __zip_all_random_access<_Views...>{}; + // expected-note@-1 {{because 'int' does not satisfy}} +}; + +zip_view<int> test_v; +static_assert(!__zip_all_random_access<int>); + +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 ed55a059..4ad3fd9 100644 --- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp +++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp @@ -106,7 +106,7 @@ concept BinaryDefaultedFalse = false; template <template <typename...> concept C, typename T> struct S { - template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}} + template <C TT> // expected-note 2{{because 'int' does not satisfy 'UnaryFalse'}} void f(TT); // expected-note {{ignored}} void g(C auto); // expected-note {{ignored}} \ // expected-note {{because 'int' does not satisfy 'UnaryFalse'}} @@ -171,7 +171,7 @@ concept BinaryDefaultedFalse = false; template <template <typename...> concept C, typename T> struct S { - template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}} + template <C TT> // expected-note 2{{because 'int' does not satisfy 'UnaryFalse'}} void f(TT); // expected-note {{ignored}} void g(C auto); // expected-note {{ignored}} \ // expected-note {{because 'int' does not satisfy 'UnaryFalse'}} diff --git a/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp b/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp index 436dfb9..8400340 100644 --- a/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp +++ b/clang/test/SemaCXX/invalid-requirement-requires-expr.cpp @@ -1,6 +1,6 @@ // RUN: %clang -fsyntax-only -std=c++2a -Xclang -verify -ftemplate-depth=5 -ftemplate-backtrace-limit=4 %s -// RequiresExpr contains invalid requirement. (Eg. Highly recurisive template). +// RequiresExpr contains invalid requirement. (Eg. Highly recursive template). template<int x> struct A { static constexpr bool far(); }; class B { @@ -19,7 +19,7 @@ constexpr bool A<x>::far() { // expected-error@#Invalid {{recursive template instantiation exceeded maximum depth}} // expected-note@#Invalid 3 {{while}} // expected-note@#Invalid {{contexts in backtrace}} - // expected-note@#Invalid {{increase recursive template instantiation depth}} + // expected-note@#Invalid {{use -ftemplate-depth=N to increase}} }; } static_assert(A<1>::far()); diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp index 135865c..c3bda39 100644 --- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp +++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp @@ -102,7 +102,7 @@ static_assert(__is_constructible(Movable, int)); // expected-error@-1 {{no matching constructor for initialization of 'Movable'}} \ // expected-note@-1 2{{}} // expected-error@#err-self-constraint-1{{satisfaction of constraint '__is_constructible(Movable, T)' depends on itself}} -// expected-note@#err-self-constraint-1 4{{}} +// expected-note@#err-self-constraint-1 3{{}} // expected-note@#Movable {{'Movable' defined here}} template <typename T> @@ -200,7 +200,6 @@ void h(short n) { f(n); } // expected-note@-1{{while checking constraint satisfaction for template}} // expected-note@#GH62096-note1{{in instantiation}} // expected-note@#GH62096-note1{{while substituting template arguments into constraint expression here}} -// expected-note@#GH62096-note2{{while substituting template arguments into constraint expression here}} // expected-note@#GH62096-note2{{while checking the satisfaction of concept}} // expected-note@#GH62096-err {{expression evaluates}} } diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index d49330f..901d510 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -5129,12 +5129,12 @@ namespace GH121278 { #if __cplusplus >= 202002L template <typename B, typename D> concept C = __is_base_of(B, D); -// expected-error@-1 {{incomplete type 'GH121278::S' used in type trait expression}} +// expected-error@-1 {{incomplete type 'S' used in type trait expression}} // expected-note@-2 {{while substituting template arguments into constraint expression here}} struct T; struct S; bool b = C<T, S>; -// expected-note@-1 {{while checking the satisfaction of concept 'C<GH121278::T, GH121278::S>' requested here}} +// expected-note@-1 {{while checking the satisfaction of concept 'C<T, S>' requested here}} #endif } diff --git a/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl b/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl index d7c6876..999372c 100644 --- a/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl +++ b/clang/test/SemaHLSL/BuiltIns/Buffers.hlsl @@ -19,7 +19,7 @@ Buffer<double2> r4; // expected-error@+4 {{constraints not satisfied for class template 'Buffer'}} // expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class Buffer}} -// expected-note@*:* {{because 'hlsl::Buffer<int>' does not satisfy '__is_typed_resource_element_compatible'}} +// expected-note@*:* {{because 'Buffer<int>' does not satisfy '__is_typed_resource_element_compatible'}} // expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(hlsl::Buffer<int>)' evaluated to false}} Buffer<Buffer<int> > r5; @@ -65,7 +65,7 @@ Buffer<half[4]> r10; typedef vector<int, 8> int8; // expected-error@+3 {{constraints not satisfied for class template 'Buffer'}} -// expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}} +// expected-note@*:* {{because 'int8' (aka 'vector<int, 8>') does not satisfy '__is_typed_resource_element_compatible'}} // expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<int, 8>)' evaluated to false}} Buffer<int8> r11; @@ -90,7 +90,7 @@ enum numbers { one, two, three }; Buffer<numbers> r15; // expected-error@+3 {{constraints not satisfied for class template 'Buffer'}} -// expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}} +// expected-note@*:* {{because 'double3' (aka 'vector<double, 3>') does not satisfy '__is_typed_resource_element_compatible'}} // expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<double, 3>)' evaluated to false}} Buffer<double3> r16; diff --git a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl index 361f4303..b33f2af 100644 --- a/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl +++ b/clang/test/SemaHLSL/BuiltIns/RWBuffers.hlsl @@ -19,7 +19,7 @@ RWBuffer<double2> r4; // expected-error@+4 {{constraints not satisfied for class template 'RWBuffer'}} // expected-note@*:* {{template declaration from hidden source: template <typename element_type> requires __is_typed_resource_element_compatible<element_type> class RWBuffer}} -// expected-note@*:* {{because 'hlsl::RWBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}} +// expected-note@*:* {{because 'RWBuffer<int>' does not satisfy '__is_typed_resource_element_compatible'}} // expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(hlsl::RWBuffer<int>)' evaluated to false}} RWBuffer<RWBuffer<int> > r5; @@ -65,7 +65,7 @@ RWBuffer<half[4]> r10; typedef vector<int, 8> int8; // expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}} -// expected-note@*:* {{because 'vector<int, 8>' (vector of 8 'int' values) does not satisfy '__is_typed_resource_element_compatible'}} +// expected-note@*:* {{because 'int8' (aka 'vector<int, 8>') does not satisfy '__is_typed_resource_element_compatible'}} // expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<int, 8>)' evaluated to false}} RWBuffer<int8> r11; @@ -90,7 +90,7 @@ enum numbers { one, two, three }; RWBuffer<numbers> r15; // expected-error@+3 {{constraints not satisfied for class template 'RWBuffer'}} -// expected-note@*:* {{because 'vector<double, 3>' (vector of 3 'double' values) does not satisfy '__is_typed_resource_element_compatible'}} +// expected-note@*:* {{because 'double3' (aka 'vector<double, 3>') does not satisfy '__is_typed_resource_element_compatible'}} // expected-note@*:* {{because '__builtin_hlsl_is_typed_resource_element_compatible(vector<double, 3>)' evaluated to false}} RWBuffer<double3> r16; diff --git a/clang/test/SemaTemplate/GH161657.cpp b/clang/test/SemaTemplate/GH161657.cpp new file mode 100644 index 0000000..5ad4dde --- /dev/null +++ b/clang/test/SemaTemplate/GH161657.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -triple=x86_64 -fsyntax-only -std=c++20 -ffp-exception-behavior=strict -verify %s +// expected-no-diagnostics + +template <class T> struct S { + template <class U> using type1 = decltype([] { return U{}; }); +}; + +void foo() { + using T1 = S<int>::type1<int>; + int x = T1()(); +} diff --git a/clang/test/SemaTemplate/concepts-recovery-expr.cpp b/clang/test/SemaTemplate/concepts-recovery-expr.cpp index 6bed179..aa4ed53 100644 --- a/clang/test/SemaTemplate/concepts-recovery-expr.cpp +++ b/clang/test/SemaTemplate/concepts-recovery-expr.cpp @@ -4,7 +4,7 @@ constexpr bool CausesRecoveryExpr = "test" + 1.0f; template<typename T> -concept ReferencesCRE = CausesRecoveryExpr; +concept ReferencesCRE = CausesRecoveryExpr; // #subst1 template<typename T> requires CausesRecoveryExpr // #NVC1REQ void NoViableCands1(){} // #NVC1 @@ -19,16 +19,18 @@ void NVCUse() { NoViableCands1<int>(); // expected-error@-1 {{no matching function for call to 'NoViableCands1'}} // expected-note@#NVC1{{candidate template ignored: constraints not satisfied}} + // expected-note@#NVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}} // expected-note@#NVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} NoViableCands2<int>(); // expected-error@-1 {{no matching function for call to 'NoViableCands2'}} // expected-note@#NVC2{{candidate template ignored: constraints not satisfied}} - // expected-note@#NVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} NoViableCands3<int>(); // expected-error@-1 {{no matching function for call to 'NoViableCands3'}} // expected-note@#NVC3{{candidate template ignored: constraints not satisfied}} - // expected-note@#NVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#NVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} } template<typename T> requires CausesRecoveryExpr // #OVC1REQ @@ -58,12 +60,14 @@ void OVCUse() { // expected-error@-1 {{no matching function for call to 'OtherViableCands2'}} // expected-note@#OVC2_ALT {{candidate function}} // expected-note@#OVC2 {{candidate template ignored: constraints not satisfied}} - // expected-note@#OVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#OVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} OtherViableCands3<int>(); // expected-error@-1 {{no matching function for call to 'OtherViableCands3'}} // expected-note@#OVC3_ALT {{candidate function}} // expected-note@#OVC3 {{candidate template ignored: constraints not satisfied}} - // expected-note@#OVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#OVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} } template<typename T> requires CausesRecoveryExpr // #OBNVC1REQ @@ -95,13 +99,15 @@ void OBNVCUse() { // expected-note@#OBNVC2_ALT {{candidate template ignored: constraints not satisfied}} // expected-note@#OBNVC2REQ_ALT {{because 'false' evaluated to false}} // expected-note@#OBNVC2 {{candidate template ignored: constraints not satisfied}} - // expected-note@#OBNVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#OBNVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} OtherBadNoViableCands3<int>(); // expected-error@-1 {{no matching function for call to 'OtherBadNoViableCands3'}} // expected-note@#OBNVC3_ALT {{candidate template ignored: constraints not satisfied}} // expected-note@#OBNVC3REQ_ALT {{because 'false' evaluated to false}} // expected-note@#OBNVC3 {{candidate template ignored: constraints not satisfied}} - // expected-note@#OBNVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#OBNVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} } @@ -136,12 +142,14 @@ void MemOVCUse() { // expected-error@-1 {{no matching member function for call to 'OtherViableCands2'}} // expected-note@#MEMOVC2_ALT {{candidate function}} // expected-note@#MEMOVC2 {{candidate template ignored: constraints not satisfied}} - // expected-note@#MEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#MEMOVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} S.OtherViableCands3<int>(); // expected-error@-1 {{no matching member function for call to 'OtherViableCands3'}} // expected-note@#MEMOVC3_ALT {{candidate function}} // expected-note@#MEMOVC3 {{candidate template ignored: constraints not satisfied}} - // expected-note@#MEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#MEMOVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} } struct StaticOVC { @@ -173,12 +181,14 @@ void StaticMemOVCUse() { // expected-error@-1 {{no matching function for call to 'OtherViableCands2'}} // expected-note@#SMEMOVC2_ALT {{candidate function}} // expected-note@#SMEMOVC2 {{candidate template ignored: constraints not satisfied}} - // expected-note@#SMEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#SMEMOVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} StaticOVC::OtherViableCands3<int>(); // expected-error@-1 {{no matching function for call to 'OtherViableCands3'}} // expected-note@#SMEMOVC3_ALT {{candidate function}} // expected-note@#SMEMOVC3 {{candidate template ignored: constraints not satisfied}} - // expected-note@#SMEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#SMEMOVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}} + // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} } namespace GH58548 { diff --git a/clang/test/SemaTemplate/concepts-recursive-inst.cpp b/clang/test/SemaTemplate/concepts-recursive-inst.cpp index 097cad1..73dce93 100644 --- a/clang/test/SemaTemplate/concepts-recursive-inst.cpp +++ b/clang/test/SemaTemplate/concepts-recursive-inst.cpp @@ -12,7 +12,7 @@ void g() { // expected-note@#FDEF{{because 'int' does not satisfy 'c'}} // expected-note@#CDEF{{because 'f(t)' would be invalid: no matching function for call to 'f'}} } -} // namespace GH53213 +} // namespace GH53213 namespace GH45736 { struct constrained; @@ -67,15 +67,14 @@ struct my_range{ void baz() { auto it = begin(rng); // #BEGIN_CALL -// expected-error@#INF_BEGIN {{satisfaction of constraint 'Inf<Inf auto>' depends on itself}} -// expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}} +// expected-error-re@#INF_REQ {{satisfaction of constraint {{.*}} depends on itself}} +// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<DirectRecursiveCheck::my_range>' requested here}} // expected-note@#INF_BEGIN_EXPR {{while checking constraint satisfaction for template 'begin<DirectRecursiveCheck::my_range>' required here}} // expected-note@#INF_BEGIN_EXPR {{while substituting deduced template arguments into function template 'begin'}} // expected-note@#INF_BEGIN_EXPR {{in instantiation of requirement here}} // expected-note@#INF_REQ {{while substituting template arguments into constraint expression here}} -// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<DirectRecursiveCheck::my_range>' requested here}} -// expected-note@#INF_BEGIN {{while substituting template arguments into constraint expression here}} -// expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin<DirectRecursiveCheck::my_range>' required here}} +// expected-note@#INF_BEGIN {{while checking the satisfaction of concept 'Inf<struct my_range>' requested here}} +// expected-note@#BEGIN_CALL {{while checking constraint satisfaction for template 'begin<struct my_range>' required here}} // expected-note@#BEGIN_CALL {{while substituting deduced template arguments into function template}} // Fallout of the failure is failed lookup, which is necessary to stop odd @@ -83,6 +82,7 @@ auto it = begin(rng); // #BEGIN_CALL // expected-error@#BEGIN_CALL {{no matching function for call to 'begin'}} // expected-note@#NOTINF_BEGIN {{candidate function}} // expected-note@#INF_BEGIN{{candidate template ignored: constraints not satisfied}} +// expected-note@#INF_BEGIN{{because 'Inf auto' does not satisfy 'Inf}} } } // namespace DirectRecursiveCheck @@ -100,16 +100,17 @@ namespace GH50891 { static_assert(Numeric<Deferred>); // #STATIC_ASSERT // expected-error@#NUMERIC{{satisfaction of constraint 'requires (T a) { foo(a); }' depends on itself}} // expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}} - // expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric<GH50891::Deferred>' requested here}} - // expected-note@#OP_TO {{while substituting template arguments into constraint expression here}} - // expected-note@#FOO_CALL {{while checking constraint satisfaction for template}} - // expected-note@#FOO_CALL {{while substituting deduced template arguments into function template}} - // expected-note@#FOO_CALL {{in instantiation of requirement here}} + // expected-note@#OP_TO {{while checking the satisfaction of concept 'Numeric<Deferred>' requested here}} + // expected-note@#OP_TO {{skipping 1 context}} + // expected-note@#FOO_CALL 2{{while checking constraint satisfaction for template}} + // expected-note@#FOO_CALL 2{{while substituting deduced template arguments into function template}} + // expected-note@#FOO_CALL 2{{in instantiation of requirement here}} // expected-note@#NUMERIC {{while substituting template arguments into constraint expression here}} // expected-error@#STATIC_ASSERT {{static assertion failed}} - // expected-note@#STATIC_ASSERT{{while checking the satisfaction of concept 'Numeric<GH50891::Deferred>' requested here}} - // expected-note@#STATIC_ASSERT{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}} + // expected-note@#STATIC_ASSERT{{while checking the satisfaction of concept 'Numeric<Deferred>' requested here}} + // expected-note@#STATIC_ASSERT{{because 'Deferred' does not satisfy 'Numeric'}} + // expected-note@#FOO_CALL{{because 'foo(a)' would be invalid}} } // namespace GH50891 diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 209e7dc..6d29f8b 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -1002,7 +1002,7 @@ template<class> concept Irrelevant = false; template <typename T> -concept ErrorRequires = requires(ErrorRequires auto x) { x; }; +concept ErrorRequires = requires(ErrorRequires auto x) { x; }; //#GH54678-ill-formed-concept // expected-error@-1 {{a concept definition cannot refer to itself}} \ // expected-error@-1 {{'auto' not allowed in requires expression parameter}} \ // expected-note@-1 {{declared here}} @@ -1023,8 +1023,7 @@ template<class T> void eee(T t) // expected-note {{candidate template ignored: c requires (Irrelevant<T> || Irrelevant<T> || True<T>) && False<T> {} // expected-note {{'long' does not satisfy 'False'}} template<class T> void fff(T t) // expected-note {{candidate template ignored: constraints not satisfied}} -requires((ErrorRequires<T> || False<T> || True<T>) && False<T>) {} // expected-note {{'unsigned long' does not satisfy 'False'}} - +requires((ErrorRequires<T> || False<T> || True<T>) && False<T>) {} // expected-note {{because 'unsigned long' does not satisfy 'False'}} void test() { aaa(42); // expected-error {{no matching function}} bbb(42L); // expected-error{{no matching function}} @@ -1264,12 +1263,7 @@ C auto x = 0; // expected-error@#T_Type {{type 'int' cannot be used prior to '::'}} \ // expected-note@-1 {{in instantiation of default argument}} -// This will be fixed when we merge https://github.com/llvm/llvm-project/pull/141776 -// Which makes us behave like GCC. static_assert(f(0)); -// expected-error@-1 {{no matching function for call}} \ -// expected-note@#GH61824_f {{constraints not satisfied}} \ -// expected-note@#T_Type {{type 'int' cannot be used prior to '::'}} } @@ -1278,4 +1272,65 @@ template <typename T> concept PerfectSquare = [](){} // expected-note 2{{here}} ([](auto) { return true; }) < PerfectSquare <class T>; // expected-error@-1 {{declaration of 'T' shadows template parameter}} \ // expected-error@-1 {{a concept definition cannot refer to itself}} + +} +namespace GH61811{ +template <class T> struct A { static const int x = 42; }; +template <class Ta> concept A42 = A<Ta>::x == 42; +template <class Tv> concept Void = __is_same_as(Tv, void); +template <class Tb, class Ub> concept A42b = Void<Tb> || A42<Ub>; +template <class Tc> concept R42c = A42b<Tc, Tc&>; +static_assert (R42c<void>); +} + +namespace parameter_mapping_regressions { + +namespace case1 { + +template <template <class> class> using __meval = struct __q; +template <template <class> class _Tp> +concept __mvalid = requires { typename __meval<_Tp>; }; +template <class _Fn> +concept __minvocable = __mvalid<_Fn::template __f>; +template <class...> struct __mdefer_; +template <class _Fn, class... _Args> + requires __minvocable<_Fn> +struct __mdefer_<_Fn, _Args...> {}; +template <class = __q> struct __mtransform { + template <class> using __f = int; +}; +struct __completion_domain_or_none_ : __mdefer_<__mtransform<>> {}; + +} + +namespace case2 { + +template<auto& Q, class P> concept C = Q.template operator()<P>(); +template<class P> concept E = C<[]<class Ty>{ return false; }, P>; +static_assert(!E<int>); + +} + + +namespace case3 { +template <class> constexpr bool is_move_constructible_v = false; + +template <class _Tp> +concept __cpp17_move_constructible = is_move_constructible_v<_Tp>; // #is_move_constructible_v + +template <class _Tp> +concept __cpp17_copy_constructible = __cpp17_move_constructible<_Tp>; // #__cpp17_move_constructible + +template <class _Iter> +concept __cpp17_iterator = __cpp17_copy_constructible<_Iter>; // #__cpp17_copy_constructible + +struct not_move_constructible {}; +static_assert(__cpp17_iterator<not_move_constructible>); \ +// expected-error {{static assertion failed}} \ +// expected-note {{because 'not_move_constructible' does not satisfy '__cpp17_iterator'}} \ +// expected-note@#__cpp17_copy_constructible {{because 'not_move_constructible' does not satisfy '__cpp17_copy_constructible'}} \ +// expected-note@#__cpp17_move_constructible {{because 'parameter_mapping_regressions::case3::not_move_constructible' does not satisfy '__cpp17_move_constructible'}} \ +// expected-note@#is_move_constructible_v {{because 'is_move_constructible_v<parameter_mapping_regressions::case3::not_move_constructible>' evaluated to false}} +} + } diff --git a/clang/test/SemaTemplate/deduction-guide.cpp b/clang/test/SemaTemplate/deduction-guide.cpp index e2b586e..9e5756f 100644 --- a/clang/test/SemaTemplate/deduction-guide.cpp +++ b/clang/test/SemaTemplate/deduction-guide.cpp @@ -574,8 +574,9 @@ static_assert(x.size == 4); // CHECK-NEXT: | |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'U (&)[3]' // CHECK-NEXT: | `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True' // CHECK-NEXT: | |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28 -// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-0-0' -// CHECK-NEXT: | | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0 +// CHECK-NEXT: | | `-TemplateArgument type 'T' +// CHECK-NEXT: | | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0 +// CHECK-NEXT: | | `-TemplateTypeParm 0x{{.+}} 'T' // CHECK-NEXT: | `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0' // CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0 // CHECK-NEXT: | `-TemplateTypeParm 0x{{.+}} 'T' @@ -588,8 +589,9 @@ static_assert(x.size == 4); // CHECK-NEXT: |-ParmVarDecl 0x{{.+}} <col:18, col:24> col:21 'double (&)[3]' // CHECK-NEXT: `-ConceptSpecializationExpr 0x{{.+}} <col:36, col:42> 'bool' Concept 0x{{.+}} 'True' // CHECK-NEXT: |-ImplicitConceptSpecializationDecl 0x{{.+}} <{{.+}}> col:28 -// CHECK-NEXT: | `-TemplateArgument type 'type-parameter-0-0' -// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'type-parameter-0-0' dependent depth 0 index 0 +// CHECK-NEXT: | `-TemplateArgument type 'T' +// CHECK-NEXT: | `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0 +// CHECK-NEXT: | `-TemplateTypeParm 0x{{.+}} 'T' // CHECK-NEXT: `-TemplateArgument <{{.+}}> type 'T':'type-parameter-0-0' // CHECK-NEXT: `-TemplateTypeParmType 0x{{.+}} 'T' dependent depth 0 index 0 // CHECK-NEXT: `-TemplateTypeParm 0x{{.+}} 'T' @@ -660,8 +662,9 @@ Test test(42); // CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} Concept {{.*}} 'Constraint' depth 0 index 1 auto:1 // CHECK-NEXT: | `-ConceptSpecializationExpr {{.*}} 'bool' Concept {{.*}} 'Constraint' // CHECK-NEXT: | |-ImplicitConceptSpecializationDecl {{.*}} -// CHECK-NEXT: | | |-TemplateArgument type 'type-parameter-0-1' -// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent depth 0 index 1 +// CHECK-NEXT: | | |-TemplateArgument type 'auto:1' +// CHECK-NEXT: | | | `-TemplateTypeParmType {{.*}} 'auto:1' dependent depth 0 index 1 +// CHECK-NEXT: | | | `-TemplateTypeParm {{.*}} 'auto:1' // CHECK-NEXT: | | `-TemplateArgument type 'int' // CHECK-NEXT: | | `-BuiltinType {{.*}} 'int' // CHECK-NEXT: | |-TemplateArgument {{.*}} type 'auto:1':'type-parameter-0-1' diff --git a/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp b/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp index 1f2171a..e03756e 100644 --- a/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp +++ b/clang/test/SemaTemplate/instantiate-abbreviated-template.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -std=c++2a -x c++ %s -verify + template<typename...> concept C = false; // expected-note 9{{because}} diff --git a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp index 3edf243..de4a484 100644 --- a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp +++ b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp @@ -7,8 +7,7 @@ template<typename T> constexpr bool is_same_v<T, T> = true; template<typename T, typename U> -concept same_as = is_same_v<T, U>; -// expected-note@-1{{because 'is_same_v<int, bool>' evaluated to false}} +concept same_as = is_same_v<T, U>; //#is_same_v template<typename T, typename... Us> concept either = (is_same_v<T, Us> || ...); @@ -17,6 +16,7 @@ template<typename... Ts> struct T { template<same_as<Ts>... Us> // expected-note@-1{{because 'same_as<int, bool>' evaluated to false}} + // expected-note@#is_same_v{{because 'is_same_v<int, bool>' evaluated to false}} static void foo(Us... u, int x) { }; // expected-note@-1{{candidate template ignored: deduced too few arguments}} // expected-note@-2{{candidate template ignored: constraints not satisfied}} diff --git a/clang/test/SemaTemplate/instantiate-requires-expr.cpp b/clang/test/SemaTemplate/instantiate-requires-expr.cpp index e60f792..32ad537 100644 --- a/clang/test/SemaTemplate/instantiate-requires-expr.cpp +++ b/clang/test/SemaTemplate/instantiate-requires-expr.cpp @@ -72,12 +72,12 @@ namespace type_requirement { template<typename T> requires false_v<requires { typename T::template temp<T>; }> - // expected-note@-1 {{because 'false_v<requires { typename type_requirement::contains_template<int>::template temp<type_requirement::contains_template<int>>; }>' evaluated to false}} - // expected-note@-2 {{because 'false_v<requires { typename type_requirement::contains_template<short>::template temp<type_requirement::contains_template<short>>; }>' evaluated to false}} + // expected-note@-1 {{because 'false_v<requires { typename contains_template<int>::template temp<contains_template<int>>; }>' evaluated to false}} + // expected-note@-2 {{because 'false_v<requires { typename contains_template<short>::template temp<contains_template<short>>; }>' evaluated to false}} struct r2 {}; - using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<int>]}} - using r2i2 = r2<contains_template<short>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<short>]}} + using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = contains_template<int>]}} + using r2i2 = r2<contains_template<short>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = contains_template<short>]}} // substitution error occurs, then requires expr is instantiated again @@ -108,7 +108,7 @@ namespace type_requirement { // expected-note@-1 {{because 'false_v<requires { <<error-type>>; } && requires { <<error-type>>; }>' evaluated to false}} struct r7 {}; - using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, type_requirement::A>]}} + using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, A>]}} } namespace expr_requirement { @@ -268,3 +268,13 @@ struct Foo { }; } // namespace GH110785 + +namespace sugared_instantiation { + template <class C1> concept C = requires { C1{}; }; + template <class D1> concept D = requires { new D1; }; + + // Test that 'deduced auto' doesn't get confused with 'undeduced auto'. + auto f() { return 0; } + static_assert(requires { { f() } -> C; }); + static_assert(requires { { f() } -> D; }); +} // namespace sugared_instantiation diff --git a/clang/test/SemaTemplate/instantiate-template-argument.cpp b/clang/test/SemaTemplate/instantiate-template-argument.cpp index 43d5d00..7606619 100644 --- a/clang/test/SemaTemplate/instantiate-template-argument.cpp +++ b/clang/test/SemaTemplate/instantiate-template-argument.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify +// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify=expected,cxx20 +// RUN: %clang_cc1 -std=c++2c -x c++ %s -verify + template<auto T, decltype(T) U> concept C1 = sizeof(U) >= 4; @@ -9,20 +11,101 @@ concept C2 = C1<Y{}, V>; // sizeof(U) >= 4 [U = V (decltype(Y{}))] template<char W> -constexpr int foo() requires C2<int, W> { return 1; } +constexpr int foo() requires C2<int, W> { return 1; } // #cand1 // sizeof(U) >= 4 [U = W (decltype(int{}))] template<char X> -// expected-note@+1{{candidate function}} -constexpr int foo() requires C1<1, X> && true { return 2; } +constexpr int foo() requires C1<1, X> && true { return 2; } // #cand2 // sizeof(U) >= 4 [U = X (decltype(1))] static_assert(foo<'a'>() == 2); + template<char Z> -// expected-note@+1{{candidate function}} -constexpr int foo() requires C2<long long, Z> && true { return 3; } +constexpr int foo() requires C2<long long, Z> && true { return 3; } // #cand3 // sizeof(U) >= 4 [U = Z (decltype(long long{}))] static_assert(foo<'a'>() == 3); -// expected-error@-1{{call to 'foo' is ambiguous}}
\ No newline at end of file +// expected-error@-1{{call to 'foo' is ambiguous}} +// expected-note@#cand2 {{candidate function}} +// expected-note@#cand3 {{candidate function}} + + +namespace case1 { + +template<auto T, decltype(T) U> +concept C1 = sizeof(T) >= 4; // #case1_C1 + +template<typename Y, char V> +concept C2 = C1<Y{}, V>; // #case1_C2 + +template<class T, char W> +constexpr int foo() requires C2<T, W> { return 1; } // #case1_foo1 + +template<class T, char X> +constexpr int foo() requires C1<T{}, X> && true { return 2; } // #case1_foo2 + +static_assert(foo<char, 'a'>() == 2); +// expected-error@-1{{no matching function for call to 'foo'}} +// expected-note@#case1_foo1{{candidate template ignored: constraints not satisfied [with T = char, W = 'a']}} +// expected-note@#case1_foo1{{because 'C2<char, 'a'>' evaluated to false}} +// expected-note@#case1_C2{{because 'C1<char{}, 'a'>' evaluated to false}} +// expected-note@#case1_C1{{because 'sizeof ('\x00') >= 4' (1 >= 4) evaluated to false}} +// expected-note@#case1_foo2{{candidate template ignored: constraints not satisfied [with T = char, X = 'a']}} +// expected-note@#case1_foo2{{because 'C1<char{}, 'a'>' evaluated to false}} +// expected-note@#case1_C1{{because 'sizeof ('\x00') >= 4' (1 >= 4) evaluated to false}} + +static_assert(foo<int, 'a'>() == 2); + +} + +namespace packs { + +template<auto T, decltype(T) U> +concept C1 = sizeof(U) >= 4; + +template<typename Y, char V> +concept C2 = C1<Y{}, V>; + +template<char... W> +constexpr int foo() requires (C2<int, W> && ...) { return 1; } // #packs-cand1 + +template<char... X> +constexpr int foo() requires (C1<1, X> && ...) && true { return 2; } // #packs-cand2 + +static_assert(foo<'a'>() == 2); +// cxx20-error@-1{{call to 'foo' is ambiguous}} +// cxx20-note@#packs-cand1 {{candidate function}} +// cxx20-note@#packs-cand2 {{candidate function}} + +} + +namespace case2 { +template<auto T> concept C1 = sizeof(decltype(T)) >= 0; +template<typename Y> concept C2 = C1<Y{}>; + +template<char W> +constexpr int foo() requires C2<int> { return 1; } + +template<char X> +constexpr int foo() requires C1<0> && true { return 2; } + +static_assert(foo<0>() == 2); +} + +namespace case3 { +template<auto T> concept C1 = sizeof(decltype(T)) >= 0; + +template<typename Y> concept C2 = C1<Y{}>; + +template<char W> +constexpr int foo() requires C2<int> { return 1; } // #case3_foo1 + +template<char X> +constexpr int foo() requires C1<1> && true { return 2; } // #case3_foo2 + +static_assert(foo<0>() == 2); +// expected-error@-1{{call to 'foo' is ambiguous}} +// expected-note@#case3_foo1 {{candidate function}} +// expected-note@#case3_foo2 {{candidate function}} +} diff --git a/clang/test/SemaTemplate/pr52970.cpp b/clang/test/SemaTemplate/pr52970.cpp index 7aac5ee..6aabc41 100644 --- a/clang/test/SemaTemplate/pr52970.cpp +++ b/clang/test/SemaTemplate/pr52970.cpp @@ -53,7 +53,7 @@ static_assert(!DotFollowingPointer::f(Bad{}), ""); #if __cplusplus >= 202002L template <class T> concept C = requires(T t) { t.begin(); }; - // cxx20-note@-1 {{because 't.begin()' would be invalid: member reference type 'Holder<Incomplete> *' is a pointer}} + // cxx20-note@-1 {{because 't.begin()' would be invalid: member reference type 'Bad' (aka 'Holder<Incomplete> *') is a pointer}} static_assert(C<Good>); static_assert(!C<Bad>); |