diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d4d4a82525a02..be4aebf1d04e2 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -168,6 +168,7 @@ class PseudoDestructorTypeStorage; class PseudoObjectExpr; class QualType; class SemaCodeCompletion; +class SemaConcept; class SemaCUDA; class SemaHLSL; class SemaObjC; @@ -181,6 +182,7 @@ class SwitchStmt; class TemplateArgument; class TemplateArgumentList; class TemplateArgumentLoc; +class TemplateCompareNewDeclInfo; class TemplateDecl; class TemplateInstantiationCallback; class TemplateParameterList; @@ -989,6 +991,11 @@ class Sema final : public SemaBase { return *CodeCompletionPtr; } + SemaConcept &Concept() { + assert(ConceptPtr); + return *ConceptPtr; + } + SemaCUDA &CUDA() { assert(CUDAPtr); return *CUDAPtr; @@ -1050,6 +1057,7 @@ class Sema final : public SemaBase { mutable IdentifierInfo *Ident_super; std::unique_ptr CodeCompletionPtr; + std::unique_ptr ConceptPtr; std::unique_ptr CUDAPtr; std::unique_ptr HLSLPtr; std::unique_ptr ObjCPtr; @@ -2028,8 +2036,6 @@ class Sema final : public SemaBase { void CheckTCBEnforcement(const SourceLocation CallExprLoc, const NamedDecl *Callee); - void CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc); - private: void CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr, const ArraySubscriptExpr *ASE = nullptr, @@ -4727,9 +4733,6 @@ class Sema final : public SemaBase { void SetFunctionBodyKind(Decl *D, SourceLocation Loc, FnBodyKind BodyKind, StringLiteral *DeletedMessage = nullptr); - void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D); - ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr); - ExprResult ActOnRequiresClause(ExprResult ConstraintExpr); NamedDecl * ActOnDecompositionDeclarator(Scope *S, Declarator &D, @@ -6811,45 +6814,6 @@ class Sema final : public SemaBase { bool IsIfExists, CXXScopeSpec &SS, UnqualifiedId &Name); - RequiresExprBodyDecl * - ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, - ArrayRef LocalParameters, - Scope *BodyScope); - void ActOnFinishRequiresExpr(); - concepts::Requirement *ActOnSimpleRequirement(Expr *E); - concepts::Requirement *ActOnTypeRequirement(SourceLocation TypenameKWLoc, - CXXScopeSpec &SS, - SourceLocation NameLoc, - const IdentifierInfo *TypeName, - TemplateIdAnnotation *TemplateId); - concepts::Requirement *ActOnCompoundRequirement(Expr *E, - SourceLocation NoexceptLoc); - concepts::Requirement *ActOnCompoundRequirement( - Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS, - TemplateIdAnnotation *TypeConstraint, unsigned Depth); - concepts::Requirement *ActOnNestedRequirement(Expr *Constraint); - concepts::ExprRequirement *BuildExprRequirement( - Expr *E, bool IsSatisfied, SourceLocation NoexceptLoc, - concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement); - concepts::ExprRequirement *BuildExprRequirement( - concepts::Requirement::SubstitutionDiagnostic *ExprSubstDiag, - bool IsSatisfied, SourceLocation NoexceptLoc, - concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement); - concepts::TypeRequirement *BuildTypeRequirement(TypeSourceInfo *Type); - concepts::TypeRequirement *BuildTypeRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag); - concepts::NestedRequirement *BuildNestedRequirement(Expr *E); - concepts::NestedRequirement * - BuildNestedRequirement(StringRef InvalidConstraintEntity, - const ASTConstraintSatisfaction &Satisfaction); - ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc, - RequiresExprBodyDecl *Body, - SourceLocation LParenLoc, - ArrayRef LocalParameters, - SourceLocation RParenLoc, - ArrayRef Requirements, - SourceLocation ClosingBraceLoc); - private: ExprResult BuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult, bool IsDelete); @@ -8847,30 +8811,6 @@ class Sema final : public SemaBase { unsigned Position, SourceLocation EqualLoc, ParsedType DefaultArg, bool HasTypeConstraint); - bool CheckTypeConstraint(TemplateIdAnnotation *TypeConstraint); - - bool ActOnTypeConstraint(const CXXScopeSpec &SS, - TemplateIdAnnotation *TypeConstraint, - TemplateTypeParmDecl *ConstrainedParameter, - SourceLocation EllipsisLoc); - bool BuildTypeConstraint(const CXXScopeSpec &SS, - TemplateIdAnnotation *TypeConstraint, - TemplateTypeParmDecl *ConstrainedParameter, - SourceLocation EllipsisLoc, - bool AllowUnexpandedPack); - - bool AttachTypeConstraint(NestedNameSpecifierLoc NS, - DeclarationNameInfo NameInfo, - ConceptDecl *NamedConcept, NamedDecl *FoundDecl, - const TemplateArgumentListInfo *TemplateArgs, - TemplateTypeParmDecl *ConstrainedParameter, - SourceLocation EllipsisLoc); - - bool AttachTypeConstraint(AutoTypeLoc TL, - NonTypeTemplateParmDecl *NewConstrainedParm, - NonTypeTemplateParmDecl *OrigConstrainedParm, - SourceLocation EllipsisLoc); - bool RequireStructuralType(QualType T, SourceLocation Loc); QualType CheckNonTypeTemplateParameterType(TypeSourceInfo *&TSI, @@ -8974,12 +8914,6 @@ class Sema final : public SemaBase { SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs); - ExprResult - CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - const DeclarationNameInfo &ConceptNameInfo, - NamedDecl *FoundDecl, ConceptDecl *NamedConcept, - const TemplateArgumentListInfo *TemplateArgs); - void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc); ExprResult BuildTemplateIdExpr(const CXXScopeSpec &SS, @@ -9198,46 +9132,6 @@ class Sema final : public SemaBase { TPL_TemplateParamsEquivalent, }; - // A struct to represent the 'new' declaration, which is either itself just - // the named decl, or the important information we need about it in order to - // do constraint comparisons. - class TemplateCompareNewDeclInfo { - const NamedDecl *ND = nullptr; - const DeclContext *DC = nullptr; - const DeclContext *LexicalDC = nullptr; - SourceLocation Loc; - - public: - TemplateCompareNewDeclInfo(const NamedDecl *ND) : ND(ND) {} - TemplateCompareNewDeclInfo(const DeclContext *DeclCtx, - const DeclContext *LexicalDeclCtx, - SourceLocation Loc) - - : DC(DeclCtx), LexicalDC(LexicalDeclCtx), Loc(Loc) { - assert(DC && LexicalDC && - "Constructor only for cases where we have the information to put " - "in here"); - } - - // If this was constructed with no information, we cannot do substitution - // for constraint comparison, so make sure we can check that. - bool isInvalid() const { return !ND && !DC; } - - const NamedDecl *getDecl() const { return ND; } - - bool ContainsDecl(const NamedDecl *ND) const { return this->ND == ND; } - - const DeclContext *getLexicalDeclContext() const { - return ND ? ND->getLexicalDeclContext() : LexicalDC; - } - - const DeclContext *getDeclContext() const { - return ND ? ND->getDeclContext() : DC; - } - - SourceLocation getLocation() const { return ND ? ND->getLocation() : Loc; } - }; - bool TemplateParameterListsAreEqual( const TemplateCompareNewDeclInfo &NewInstFrom, TemplateParameterList *New, const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain, @@ -9361,15 +9255,6 @@ class Sema final : public SemaBase { void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD); - Decl *ActOnConceptDefinition(Scope *S, - MultiTemplateParamsArg TemplateParameterLists, - const IdentifierInfo *Name, - SourceLocation NameLoc, Expr *ConstraintExpr, - const ParsedAttributesView &Attrs); - - void CheckConceptRedefinition(ConceptDecl *NewDecl, LookupResult &Previous, - bool &AddToScope); - TypeResult ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK, const CXXScopeSpec &SS, const IdentifierInfo *Name, @@ -9386,6 +9271,9 @@ class Sema final : public SemaBase { void checkSpecializationVisibility(SourceLocation Loc, NamedDecl *Spec); void checkSpecializationReachability(SourceLocation Loc, NamedDecl *Spec); + TemplateArgumentListInfo + makeTemplateArgumentListInfo(TemplateIdAnnotation &TemplateId); + ///@} // @@ -9653,9 +9541,6 @@ class Sema final : public SemaBase { const PartialDiagnostic &CandidateDiag, bool Complain = true, QualType TargetType = QualType()); - FunctionDecl *getMoreConstrainedFunction(FunctionDecl *FD1, - FunctionDecl *FD2); - ///@} // @@ -10402,6 +10287,14 @@ class Sema final : public SemaBase { return CodeSynthesisContexts.size() > NonInstantiationEntries; } +private: + /// Introduce the instantiated captures of the lambda into the local + /// instantiation scope. + bool addInstantiatedCapturesToScope( + FunctionDecl *Function, const FunctionDecl *PatternDecl, + LocalInstantiationScope &Scope, + const MultiLevelTemplateArgumentList &TemplateArgs); + ///@} // @@ -10638,12 +10531,6 @@ class Sema final : public SemaBase { const DeclContext *Pattern, const MultiLevelTemplateArgumentList &TemplateArgs); -private: - /// Introduce the instantiated local variables into the local - /// instantiation scope. - void addInstantiatedLocalVarsToScope(FunctionDecl *Function, - const FunctionDecl *PatternDecl, - LocalInstantiationScope &Scope); /// Introduce the instantiated function parameters into the local /// instantiation scope, and set the parameter names to those used /// in the template. @@ -10652,6 +10539,13 @@ class Sema final : public SemaBase { LocalInstantiationScope &Scope, const MultiLevelTemplateArgumentList &TemplateArgs); +private: + /// Introduce the instantiated local variables into the local + /// instantiation scope. + void addInstantiatedLocalVarsToScope(FunctionDecl *Function, + const FunctionDecl *PatternDecl, + LocalInstantiationScope &Scope); + int ParsingClassDepth = 0; class SavePendingParsedClassStateRAII { @@ -11092,255 +10986,6 @@ class Sema final : public SemaBase { // // - /// \name Constraints and Concepts - /// Implementations are in SemaConcept.cpp - ///@{ - -public: - void PushSatisfactionStackEntry(const NamedDecl *D, - const llvm::FoldingSetNodeID &ID) { - const NamedDecl *Can = cast(D->getCanonicalDecl()); - SatisfactionStack.emplace_back(Can, ID); - } - - void PopSatisfactionStackEntry() { SatisfactionStack.pop_back(); } - - bool SatisfactionStackContains(const NamedDecl *D, - const llvm::FoldingSetNodeID &ID) const { - const NamedDecl *Can = cast(D->getCanonicalDecl()); - return llvm::find(SatisfactionStack, SatisfactionStackEntryTy{Can, ID}) != - SatisfactionStack.end(); - } - - using SatisfactionStackEntryTy = - std::pair; - - // Resets the current SatisfactionStack for cases where we are instantiating - // constraints as a 'side effect' of normal instantiation in a way that is not - // indicative of recursive definition. - class SatisfactionStackResetRAII { - llvm::SmallVector BackupSatisfactionStack; - Sema &SemaRef; - - public: - SatisfactionStackResetRAII(Sema &S) : SemaRef(S) { - SemaRef.SwapSatisfactionStack(BackupSatisfactionStack); - } - - ~SatisfactionStackResetRAII() { - SemaRef.SwapSatisfactionStack(BackupSatisfactionStack); - } - }; - - void SwapSatisfactionStack( - llvm::SmallVectorImpl &NewSS) { - SatisfactionStack.swap(NewSS); - } - - /// 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 - /// non-primary expression being used as an atomic constraint. - bool CheckConstraintExpression(const Expr *CE, Token NextToken = Token(), - bool *PossibleNonPrimary = nullptr, - bool IsTrailingRequiresClause = false); - - /// \brief Check whether the given list of constraint expressions are - /// satisfied (as if in a 'conjunction') given template arguments. - /// \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 TemplateArgLists the list of template arguments to substitute into - /// the constraint expression. - /// \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 ConstraintExprs, - const MultiLevelTemplateArgumentList &TemplateArgLists, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { - llvm::SmallVector Converted; - return CheckConstraintSatisfaction(Template, ConstraintExprs, 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 ConstraintExprs, - llvm::SmallVectorImpl &ConvertedConstraints, - const MultiLevelTemplateArgumentList &TemplateArgList, - SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); - - /// \brief Check whether the given non-dependent constraint expression is - /// satisfied. Returns false and updates Satisfaction with the satisfaction - /// verdict if successful, emits a diagnostic and returns true if an error - /// occurred and satisfaction could not be determined. - /// - /// \returns true if an error occurred, false otherwise. - bool CheckConstraintSatisfaction(const Expr *ConstraintExpr, - ConstraintSatisfaction &Satisfaction); - - /// Check whether the given function decl's trailing requires clause is - /// satisfied, if any. Returns false and updates Satisfaction with the - /// satisfaction verdict if successful, emits a diagnostic and returns true if - /// an error occurred and satisfaction could not be determined. - /// - /// \returns true if an error occurred, false otherwise. - bool CheckFunctionConstraints(const FunctionDecl *FD, - ConstraintSatisfaction &Satisfaction, - SourceLocation UsageLoc = SourceLocation(), - bool ForOverloadResolution = false); - - // Calculates whether two constraint expressions are equal irrespective of a - // difference in 'depth'. This takes a pair of optional 'NamedDecl's 'Old' and - // 'New', which are the "source" of the constraint, since this is necessary - // for figuring out the relative 'depth' of the constraint. The depth of the - // 'primary template' and the 'instantiated from' templates aren't necessarily - // the same, such as a case when one is a 'friend' defined in a class. - bool AreConstraintExpressionsEqual(const NamedDecl *Old, - const Expr *OldConstr, - const TemplateCompareNewDeclInfo &New, - const Expr *NewConstr); - - // Calculates whether the friend function depends on an enclosing template for - // the purposes of [temp.friend] p9. - bool FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD); - - /// \brief Ensure that the given template arguments satisfy the constraints - /// associated with the given template, emitting a diagnostic if they do not. - /// - /// \param Template The template to which the template arguments are being - /// provided. - /// - /// \param TemplateArgs The converted, canonicalized template arguments. - /// - /// \param TemplateIDRange The source range of the template id that - /// caused the constraints check. - /// - /// \returns true if the constrains are not satisfied or could not be checked - /// for satisfaction, false if the constraints are satisfied. - bool EnsureTemplateArgumentListConstraints( - TemplateDecl *Template, - const MultiLevelTemplateArgumentList &TemplateArgs, - SourceRange TemplateIDRange); - - bool CheckInstantiatedFunctionTemplateConstraints( - SourceLocation PointOfInstantiation, FunctionDecl *Decl, - ArrayRef TemplateArgs, - ConstraintSatisfaction &Satisfaction); - - /// \brief Emit diagnostics explaining why a constraint expression was deemed - /// unsatisfied. - /// \param First whether this is the first time an unsatisfied constraint is - /// diagnosed for this error. - void DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction, - bool First = true); - - /// \brief Emit diagnostics explaining why a constraint expression was deemed - /// unsatisfied. - void - DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction, - bool First = true); - - const NormalizedConstraint *getNormalizedAssociatedConstraints( - NamedDecl *ConstrainedDecl, ArrayRef AssociatedConstraints); - - /// \brief Check whether the given declaration's associated constraints are - /// at least as constrained than another declaration's according to the - /// partial ordering of constraints. - /// - /// \param Result If no error occurred, receives the result of true if D1 is - /// at least constrained than D2, and false otherwise. - /// - /// \returns true if an error occurred, false otherwise. - bool IsAtLeastAsConstrained(NamedDecl *D1, MutableArrayRef AC1, - NamedDecl *D2, MutableArrayRef AC2, - bool &Result); - - /// If D1 was not at least as constrained as D2, but would've been if a pair - /// of atomic constraints involved had been declared in a concept and not - /// repeated in two separate places in code. - /// \returns true if such a diagnostic was emitted, false otherwise. - bool MaybeEmitAmbiguousAtomicConstraintsDiagnostic( - NamedDecl *D1, ArrayRef AC1, NamedDecl *D2, - ArrayRef AC2); - -private: - /// Caches pairs of template-like decls whose associated constraints were - /// checked for subsumption and whether or not the first's constraints did in - /// fact subsume the second's. - llvm::DenseMap, bool> SubsumptionCache; - /// Caches the normalized associated constraints of declarations (concepts or - /// constrained declarations). If an error occurred while normalizing the - /// associated constraints of the template or concept, nullptr will be cached - /// here. - llvm::DenseMap NormalizationCache; - - llvm::ContextualFoldingSet - SatisfactionCache; - - // The current stack of constraint satisfactions, so we can exit-early. - llvm::SmallVector SatisfactionStack; - - /// Introduce the instantiated captures of the lambda into the local - /// instantiation scope. - bool addInstantiatedCapturesToScope( - FunctionDecl *Function, const FunctionDecl *PatternDecl, - LocalInstantiationScope &Scope, - const MultiLevelTemplateArgumentList &TemplateArgs); - - /// Used by SetupConstraintCheckingTemplateArgumentsAndScope to recursively(in - /// the case of lambdas) set up the LocalInstantiationScope of the current - /// function. - bool - SetupConstraintScope(FunctionDecl *FD, - std::optional> TemplateArgs, - const MultiLevelTemplateArgumentList &MLTAL, - LocalInstantiationScope &Scope); - - /// Used during constraint checking, sets up the constraint template argument - /// lists, and calls SetupConstraintScope to set up the - /// LocalInstantiationScope to have the proper set of ParVarDecls configured. - std::optional - SetupConstraintCheckingTemplateArgumentsAndScope( - FunctionDecl *FD, std::optional> TemplateArgs, - LocalInstantiationScope &Scope); - - ///@} - - // - // - // ------------------------------------------------------------------------- - // - // - /// \name Types /// Implementations are in SemaType.cpp ///@{ diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h index 711443505174f..96e36dc8a188c 100644 --- a/clang/include/clang/Sema/SemaConcept.h +++ b/clang/include/clang/Sema/SemaConcept.h @@ -14,17 +14,39 @@ #define LLVM_CLANG_SEMA_SEMACONCEPT_H #include "clang/AST/ASTConcept.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/Expr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/Type.h" +#include "clang/AST/TypeLoc.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Lex/Token.h" +#include "clang/Sema/DeclSpec.h" +#include "clang/Sema/Ownership.h" +#include "clang/Sema/ParsedAttr.h" +#include "clang/Sema/ParsedTemplate.h" +#include "clang/Sema/Scope.h" +#include "clang/Sema/SemaBase.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" #include #include #include namespace clang { -class Sema; struct AtomicConstraint { const Expr *ConstraintExpr; @@ -80,6 +102,7 @@ struct AtomicConstraint { /// disjunction of normalized constraints. struct NormalizedConstraint { friend class Sema; + friend class SemaConcept; enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction }; @@ -150,6 +173,368 @@ struct NormalizedConstraint { fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E); }; -} // clang +class LocalInstantiationScope; +class LookupResult; +class MultiLevelTemplateArgumentList; + +// A struct to represent the 'new' declaration, which is either itself just +// the named decl, or the important information we need about it in order to +// do constraint comparisons. +class TemplateCompareNewDeclInfo { + const NamedDecl *ND = nullptr; + const DeclContext *DC = nullptr; + const DeclContext *LexicalDC = nullptr; + SourceLocation Loc; + +public: + TemplateCompareNewDeclInfo(const NamedDecl *ND) : ND(ND) {} + TemplateCompareNewDeclInfo(const DeclContext *DeclCtx, + const DeclContext *LexicalDeclCtx, + SourceLocation Loc) + + : DC(DeclCtx), LexicalDC(LexicalDeclCtx), Loc(Loc) { + assert(DC && LexicalDC && + "Constructor only for cases where we have the information to put " + "in here"); + } + + // If this was constructed with no information, we cannot do substitution + // for constraint comparison, so make sure we can check that. + bool isInvalid() const { return !ND && !DC; } + + const NamedDecl *getDecl() const { return ND; } + + bool ContainsDecl(const NamedDecl *ND) const { return this->ND == ND; } + + const DeclContext *getLexicalDeclContext() const { + return ND ? ND->getLexicalDeclContext() : LexicalDC; + } + + const DeclContext *getDeclContext() const { + return ND ? ND->getDeclContext() : DC; + } + + SourceLocation getLocation() const { return ND ? ND->getLocation() : Loc; } +}; + +class SemaConcept : public SemaBase { +public: + SemaConcept(Sema &S); + ~SemaConcept(); + + void PushSatisfactionStackEntry(const NamedDecl *D, + const llvm::FoldingSetNodeID &ID) { + const NamedDecl *Can = cast(D->getCanonicalDecl()); + SatisfactionStack.emplace_back(Can, ID); + } + + void PopSatisfactionStackEntry() { SatisfactionStack.pop_back(); } + + bool SatisfactionStackContains(const NamedDecl *D, + const llvm::FoldingSetNodeID &ID) const { + const NamedDecl *Can = cast(D->getCanonicalDecl()); + return llvm::find(SatisfactionStack, SatisfactionStackEntryTy{Can, ID}) != + SatisfactionStack.end(); + } + + using SatisfactionStackEntryTy = + std::pair; + + // Resets the current SatisfactionStack for cases where we are instantiating + // constraints as a 'side effect' of normal instantiation in a way that is not + // indicative of recursive definition. + class SatisfactionStackResetRAII { + llvm::SmallVector BackupSatisfactionStack; + Sema &SemaRef; + + public: + SatisfactionStackResetRAII(Sema &S); + ~SatisfactionStackResetRAII(); + }; + + void SwapSatisfactionStack( + llvm::SmallVectorImpl &NewSS) { + SatisfactionStack.swap(NewSS); + } + + /// 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 + /// non-primary expression being used as an atomic constraint. + bool CheckConstraintExpression(const Expr *CE, Token NextToken = Token(), + bool *PossibleNonPrimary = nullptr, + bool IsTrailingRequiresClause = false); + + /// \brief Check whether the given list of constraint expressions are + /// satisfied (as if in a 'conjunction') given template arguments. + /// \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 TemplateArgLists the list of template arguments to substitute into + /// the constraint expression. + /// \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 ConstraintExprs, + const MultiLevelTemplateArgumentList &TemplateArgLists, + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { + llvm::SmallVector Converted; + return CheckConstraintSatisfaction(Template, ConstraintExprs, 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 ConstraintExprs, + llvm::SmallVectorImpl &ConvertedConstraints, + const MultiLevelTemplateArgumentList &TemplateArgList, + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); + + /// \brief Check whether the given non-dependent constraint expression is + /// satisfied. Returns false and updates Satisfaction with the satisfaction + /// verdict if successful, emits a diagnostic and returns true if an error + /// occurred and satisfaction could not be determined. + /// + /// \returns true if an error occurred, false otherwise. + bool CheckConstraintSatisfaction(const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction); + + /// Check whether the given function decl's trailing requires clause is + /// satisfied, if any. Returns false and updates Satisfaction with the + /// satisfaction verdict if successful, emits a diagnostic and returns true if + /// an error occurred and satisfaction could not be determined. + /// + /// \returns true if an error occurred, false otherwise. + bool CheckFunctionConstraints(const FunctionDecl *FD, + ConstraintSatisfaction &Satisfaction, + SourceLocation UsageLoc = SourceLocation(), + bool ForOverloadResolution = false); + + // Calculates whether two constraint expressions are equal irrespective of a + // difference in 'depth'. This takes a pair of optional 'NamedDecl's 'Old' and + // 'New', which are the "source" of the constraint, since this is necessary + // for figuring out the relative 'depth' of the constraint. The depth of the + // 'primary template' and the 'instantiated from' templates aren't necessarily + // the same, such as a case when one is a 'friend' defined in a class. + bool AreConstraintExpressionsEqual(const NamedDecl *Old, + const Expr *OldConstr, + const TemplateCompareNewDeclInfo &New, + const Expr *NewConstr); + + // Calculates whether the friend function depends on an enclosing template for + // the purposes of [temp.friend] p9. + bool FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD); + + /// \brief Ensure that the given template arguments satisfy the constraints + /// associated with the given template, emitting a diagnostic if they do not. + /// + /// \param Template The template to which the template arguments are being + /// provided. + /// + /// \param TemplateArgs The converted, canonicalized template arguments. + /// + /// \param TemplateIDRange The source range of the template id that + /// caused the constraints check. + /// + /// \returns true if the constrains are not satisfied or could not be checked + /// for satisfaction, false if the constraints are satisfied. + bool EnsureTemplateArgumentListConstraints( + TemplateDecl *Template, + const MultiLevelTemplateArgumentList &TemplateArgs, + SourceRange TemplateIDRange); + + bool CheckInstantiatedFunctionTemplateConstraints( + SourceLocation PointOfInstantiation, FunctionDecl *Decl, + ArrayRef TemplateArgs, + ConstraintSatisfaction &Satisfaction); + + /// \brief Emit diagnostics explaining why a constraint expression was deemed + /// unsatisfied. + /// \param First whether this is the first time an unsatisfied constraint is + /// diagnosed for this error. + void DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction, + bool First = true); + + /// \brief Emit diagnostics explaining why a constraint expression was deemed + /// unsatisfied. + void + DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction, + bool First = true); + + const NormalizedConstraint *getNormalizedAssociatedConstraints( + NamedDecl *ConstrainedDecl, ArrayRef AssociatedConstraints); + + /// \brief Check whether the given declaration's associated constraints are + /// at least as constrained than another declaration's according to the + /// partial ordering of constraints. + /// + /// \param Result If no error occurred, receives the result of true if D1 is + /// at least constrained than D2, and false otherwise. + /// + /// \returns true if an error occurred, false otherwise. + bool IsAtLeastAsConstrained(NamedDecl *D1, MutableArrayRef AC1, + NamedDecl *D2, MutableArrayRef AC2, + bool &Result); + + /// If D1 was not at least as constrained as D2, but would've been if a pair + /// of atomic constraints involved had been declared in a concept and not + /// repeated in two separate places in code. + /// \returns true if such a diagnostic was emitted, false otherwise. + bool MaybeEmitAmbiguousAtomicConstraintsDiagnostic( + NamedDecl *D1, ArrayRef AC1, NamedDecl *D2, + ArrayRef AC2); + + /// Used by SetupConstraintCheckingTemplateArgumentsAndScope to recursively(in + /// the case of lambdas) set up the LocalInstantiationScope of the current + /// function. + bool + SetupConstraintScope(FunctionDecl *FD, + std::optional> TemplateArgs, + const MultiLevelTemplateArgumentList &MLTAL, + LocalInstantiationScope &Scope); + + RequiresExprBodyDecl * + ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, + ArrayRef LocalParameters, + Scope *BodyScope); + void ActOnFinishRequiresExpr(); + concepts::Requirement *ActOnSimpleRequirement(Expr *E); + concepts::Requirement *ActOnTypeRequirement(SourceLocation TypenameKWLoc, + CXXScopeSpec &SS, + SourceLocation NameLoc, + const IdentifierInfo *TypeName, + TemplateIdAnnotation *TemplateId); + concepts::Requirement *ActOnCompoundRequirement(Expr *E, + SourceLocation NoexceptLoc); + concepts::Requirement *ActOnCompoundRequirement( + Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, unsigned Depth); + concepts::Requirement *ActOnNestedRequirement(Expr *Constraint); + concepts::ExprRequirement *BuildExprRequirement( + Expr *E, bool IsSatisfied, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement); + concepts::ExprRequirement *BuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *ExprSubstDiag, + bool IsSatisfied, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement); + concepts::TypeRequirement *BuildTypeRequirement(TypeSourceInfo *Type); + concepts::TypeRequirement *BuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag); + concepts::NestedRequirement *BuildNestedRequirement(Expr *E); + concepts::NestedRequirement * + BuildNestedRequirement(StringRef InvalidConstraintEntity, + const ASTConstraintSatisfaction &Satisfaction); + ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + SourceLocation LParenLoc, + ArrayRef LocalParameters, + SourceLocation RParenLoc, + ArrayRef Requirements, + SourceLocation ClosingBraceLoc); + + bool CheckTypeConstraint(TemplateIdAnnotation *TypeConstraint); + + bool ActOnTypeConstraint(const CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc); + bool BuildTypeConstraint(const CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc, + bool AllowUnexpandedPack); + + bool AttachTypeConstraint(NestedNameSpecifierLoc NS, + DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, NamedDecl *FoundDecl, + const TemplateArgumentListInfo *TemplateArgs, + TemplateTypeParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc); + + bool AttachTypeConstraint(AutoTypeLoc TL, + NonTypeTemplateParmDecl *NewConstrainedParm, + NonTypeTemplateParmDecl *OrigConstrainedParm, + SourceLocation EllipsisLoc); + + ExprResult + CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + const DeclarationNameInfo &ConceptNameInfo, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, + const TemplateArgumentListInfo *TemplateArgs); + + Decl *ActOnConceptDefinition(Scope *S, + MultiTemplateParamsArg TemplateParameterLists, + const IdentifierInfo *Name, + SourceLocation NameLoc, Expr *ConstraintExpr, + const ParsedAttributesView &Attrs); + + void CheckConceptRedefinition(ConceptDecl *NewDecl, LookupResult &Previous, + bool &AddToScope); + + void ActOnStartTrailingRequiresClause(Scope *S, Declarator &D); + ExprResult ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr); + ExprResult ActOnRequiresClause(ExprResult ConstraintExpr); + + void CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc); + + /// Returns the more constrained function according to the rules of + /// partial ordering by constraints (C++ [temp.constr.order]). + /// + /// \param FD1 the first function + /// + /// \param FD2 the second function + /// + /// \returns the more constrained function. If neither function is + /// more constrained, returns NULL. + FunctionDecl *getMoreConstrainedFunction(FunctionDecl *FD1, + FunctionDecl *FD2); + +private: + /// Caches pairs of template-like decls whose associated constraints were + /// checked for subsumption and whether or not the first's constraints did in + /// fact subsume the second's. + llvm::DenseMap, bool> SubsumptionCache; + /// Caches the normalized associated constraints of declarations (concepts or + /// constrained declarations). If an error occurred while normalizing the + /// associated constraints of the template or concept, nullptr will be cached + /// here. + llvm::DenseMap NormalizationCache; + + llvm::ContextualFoldingSet + SatisfactionCache; + + // The current stack of constraint satisfactions, so we can exit-early. + llvm::SmallVector SatisfactionStack; +}; + +} // namespace clang #endif // LLVM_CLANG_SEMA_SEMACONCEPT_H diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 8405b44685ae4..eb851091a14a7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -28,6 +28,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaCodeCompletion.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenMP.h" @@ -4082,8 +4083,9 @@ void Parser::ParseDeclarationSpecifiers( if (!NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) goto DoneWithDeclSpec; - if (TemplateId && !isInvalid && Actions.CheckTypeConstraint(TemplateId)) - TemplateId = nullptr; + if (TemplateId && !isInvalid && + Actions.Concept().CheckTypeConstraint(TemplateId)) + TemplateId = nullptr; ConsumeAnnotationToken(); SourceLocation AutoLoc = Tok.getLocation(); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 5eaec2b621e6f..3a923a599af22 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -4290,7 +4290,7 @@ void Parser::ParseTrailingRequiresClause(Declarator &D) { Scope::FunctionDeclarationScope | Scope::FunctionPrototypeScope); - Actions.ActOnStartTrailingRequiresClause(getCurScope(), D); + Actions.Concept().ActOnStartTrailingRequiresClause(getCurScope(), D); std::optional ThisScope; InitCXXThisScopeForDeclaratorIfRelevant(D, D.getDeclSpec(), ThisScope); @@ -4298,8 +4298,8 @@ void Parser::ParseTrailingRequiresClause(Declarator &D) { TrailingRequiresClause = ParseConstraintLogicalOrExpression(/*IsTrailingRequiresClause=*/true); - TrailingRequiresClause = - Actions.ActOnFinishTrailingRequiresClause(TrailingRequiresClause); + TrailingRequiresClause = Actions.Concept().ActOnFinishTrailingRequiresClause( + TrailingRequiresClause); if (!D.isDeclarationOfFunction()) { Diag(RequiresKWLoc, diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index eb7447fa038e4..c81a29549cb92 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -32,6 +32,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaCodeCompletion.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenACC.h" #include "clang/Sema/SemaOpenMP.h" @@ -268,7 +269,8 @@ ExprResult Parser::ParseConstraintExpression() { Actions, Sema::ExpressionEvaluationContext::Unevaluated); ExprResult LHS(ParseCastExpression(AnyCastExpr)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr)); - if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) { + if (Res.isUsable() && + !Actions.Concept().CheckConstraintExpression(Res.get())) { Actions.CorrectDelayedTyposInExpr(Res); return ExprError(); } @@ -329,9 +331,8 @@ Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) { NotPrimaryExpression = false; } bool PossibleNonPrimary; - bool IsConstraintExpr = - Actions.CheckConstraintExpression(E.get(), Tok, &PossibleNonPrimary, - IsTrailingRequiresClause); + bool IsConstraintExpr = Actions.Concept().CheckConstraintExpression( + E.get(), Tok, &PossibleNonPrimary, IsTrailingRequiresClause); if (!IsConstraintExpr || PossibleNonPrimary) { // Atomic constraint might be an unparenthesized non-primary expression // (such as a binary operator), in which case we might get here (e.g. in diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index e149b1a0fb5ef..2c240b4c20dbb 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -24,6 +24,7 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCodeCompletion.h" +#include "clang/Sema/SemaConcept.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include @@ -1417,8 +1418,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( ++CurTemplateDepthTracker; ExprResult RequiresClause; if (TryConsumeToken(tok::kw_requires)) { - RequiresClause = - Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression( + RequiresClause = Actions.Concept().ActOnRequiresClause( + ParseConstraintLogicalOrExpression( /*IsTrailingRequiresClause=*/false)); if (RequiresClause.isInvalid()) SkipUntil({tok::l_brace, tok::l_paren}, StopAtSemi | StopBeforeMatch); @@ -3628,7 +3629,7 @@ ExprResult Parser::ParseRequiresExpression() { // Dependent diagnostics are attached to this Decl and non-depenedent // diagnostics are surfaced after this parse. ParsingDeclRAIIObject ParsingBodyDecl(*this, ParsingDeclRAIIObject::NoParent); - RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr( + RequiresExprBodyDecl *Body = Actions.Concept().ActOnStartRequiresExpr( RequiresKWLoc, LocalParameterDecls, getCurScope()); if (Tok.is(tok::r_brace)) { @@ -3669,7 +3670,8 @@ ExprResult Parser::ParseRequiresExpression() { SourceLocation NoexceptLoc; TryConsumeToken(tok::kw_noexcept, NoexceptLoc); if (Tok.is(tok::semi)) { - Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc); + Req = Actions.Concept().ActOnCompoundRequirement(Expression.get(), + NoexceptLoc); if (Req) Requirements.push_back(Req); break; @@ -3696,7 +3698,7 @@ ExprResult Parser::ParseRequiresExpression() { ConsumeAnnotationToken(); } - Req = Actions.ActOnCompoundRequirement( + Req = Actions.Concept().ActOnCompoundRequirement( Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok), TemplateParameterDepth); ConsumeAnnotationToken(); @@ -3764,8 +3766,8 @@ ExprResult Parser::ParseRequiresExpression() { SkipUntilFlags::StopBeforeMatch); break; } - if (auto *Req = - Actions.ActOnNestedRequirement(ConstraintExpr.get())) + if (auto *Req = Actions.Concept().ActOnNestedRequirement( + ConstraintExpr.get())) Requirements.push_back(Req); else { SkipUntil(tok::semi, tok::r_brace, @@ -3810,9 +3812,8 @@ ExprResult Parser::ParseRequiresExpression() { break; } - if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS, - NameLoc, II, - TemplateId)) { + if (auto *Req = Actions.Concept().ActOnTypeRequirement( + TypenameKWLoc, SS, NameLoc, II, TemplateId)) { Requirements.push_back(Req); } break; @@ -3833,7 +3834,8 @@ ExprResult Parser::ParseRequiresExpression() { if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement) Diag(StartLoc, diag::err_requires_expr_in_simple_requirement) << FixItHint::CreateInsertion(StartLoc, "requires"); - if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get())) + if (auto *Req = + Actions.Concept().ActOnSimpleRequirement(Expression.get())) Requirements.push_back(Req); else { SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); @@ -3861,14 +3863,14 @@ ExprResult Parser::ParseRequiresExpression() { // other diagnostics quoting an empty requires expression they never // wrote. Braces.consumeClose(); - Actions.ActOnFinishRequiresExpr(); + Actions.Concept().ActOnFinishRequiresExpr(); return ExprError(); } } Braces.consumeClose(); - Actions.ActOnFinishRequiresExpr(); + Actions.Concept().ActOnFinishRequiresExpr(); ParsingBodyDecl.complete(Body); - return Actions.ActOnRequiresExpr( + return Actions.Concept().ActOnRequiresExpr( RequiresKWLoc, Body, Parens.getOpenLocation(), LocalParameterDecls, Parens.getCloseLocation(), Requirements, Braces.getCloseLocation()); } diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index a5130f56600e5..5aeef7a8227bb 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -20,6 +20,7 @@ #include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaDiagnostic.h" #include "llvm/Support/TimeProfiler.h" using namespace clang; @@ -145,8 +146,9 @@ Parser::DeclGroupPtrTy Parser::ParseTemplateDeclarationOrSpecialization( if (TryConsumeToken(tok::kw_requires)) { OptionalRequiresClauseConstraintER = - Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression( - /*IsTrailingRequiresClause=*/false)); + Actions.Concept().ActOnRequiresClause( + ParseConstraintLogicalOrExpression( + /*IsTrailingRequiresClause=*/false)); if (!OptionalRequiresClauseConstraintER.isUsable()) { // Skip until the semi-colon or a '}'. SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); @@ -339,9 +341,9 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, DeclEnd = Tok.getLocation(); ExpectAndConsumeSemi(diag::err_expected_semi_declaration); Expr *ConstraintExpr = ConstraintExprResult.get(); - return Actions.ActOnConceptDefinition(getCurScope(), - *TemplateInfo.TemplateParams, Id, IdLoc, - ConstraintExpr, Attrs); + return Actions.Concept().ActOnConceptDefinition( + getCurScope(), *TemplateInfo.TemplateParams, Id, IdLoc, ConstraintExpr, + Attrs); } /// ParseTemplateParameters - Parses a template-parameter-list enclosed in @@ -763,9 +765,9 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { TypeConstraint != nullptr); if (TypeConstraint) { - Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint, - cast(NewDecl), - EllipsisLoc); + Actions.Concept().ActOnTypeConstraint(TypeConstraintSS, TypeConstraint, + cast(NewDecl), + EllipsisLoc); } return NewDecl; @@ -800,8 +802,9 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth, } if (TryConsumeToken(tok::kw_requires)) { OptionalRequiresClauseConstraintER = - Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression( - /*IsTrailingRequiresClause=*/false)); + Actions.Concept().ActOnRequiresClause( + ParseConstraintLogicalOrExpression( + /*IsTrailingRequiresClause=*/false)); if (!OptionalRequiresClauseConstraintER.isUsable()) { SkipUntil(tok::comma, tok::greater, tok::greatergreater, StopAtSemi | StopBeforeMatch); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index f847c49920cf3..e3f9648db1793 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -43,6 +43,7 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaCUDA.h" #include "clang/Sema/SemaCodeCompletion.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaConsumer.h" #include "clang/Sema/SemaHLSL.h" #include "clang/Sema/SemaInternal.h" @@ -205,6 +206,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, CurScope(nullptr), Ident_super(nullptr), CodeCompletionPtr( std::make_unique(*this, CodeCompleter)), + ConceptPtr(std::make_unique(*this)), CUDAPtr(std::make_unique(*this)), HLSLPtr(std::make_unique(*this)), ObjCPtr(std::make_unique(*this)), @@ -228,7 +230,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0), AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr), InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), - ArgumentPackSubstitutionIndex(-1), SatisfactionCache(Context) { + ArgumentPackSubstitutionIndex(-1) { assert(pp.TUKind == TUKind); TUScope = nullptr; @@ -491,14 +493,6 @@ Sema::~Sema() { = dyn_cast_or_null(Context.getExternalSource())) ExternalSema->ForgetSema(); - // Delete cached satisfactions. - std::vector Satisfactions; - Satisfactions.reserve(SatisfactionCache.size()); - for (auto &Node : SatisfactionCache) - Satisfactions.push_back(&Node); - for (auto *Node : Satisfactions) - delete Node; - threadSafety::threadSafetyCleanup(ThreadSafetyDeclCache); // Destroys data sharing attributes stack for OpenMP diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index f2dc8e9dd0050..ecd1821651140 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8092,12 +8092,6 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto, diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc); } -void Sema::CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc) { - if (ConceptDecl *Decl = AutoT->getTypeConstraintConcept()) { - DiagnoseUseOfDecl(Decl, Loc); - } -} - /// CheckConstructorCall - Check a constructor call for correctness and safety /// properties not enforced by the C type system. void Sema::CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType, diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 202dd86c67f62..de7c83e353b21 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -88,9 +88,11 @@ class LogicalBinOp { }; } -bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, - Token NextToken, bool *PossibleNonPrimary, - bool IsTrailingRequiresClause) { +bool SemaConcept::CheckConstraintExpression(const Expr *ConstraintExpression, + Token NextToken, + bool *PossibleNonPrimary, + bool IsTrailingRequiresClause) { + ASTContext &Context = getASTContext(); // C++2a [temp.constr.atomic]p1 // ..E shall be a constant expression of type bool. @@ -126,7 +128,7 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, (IsTrailingRequiresClause || (Type->isDependentType() && isa(ConstraintExpression) && - !dyn_cast_if_present(getCurFunction())) || + !dyn_cast_if_present(SemaRef.getCurFunction())) || Type->isFunctionType() || Type->isSpecificBuiltinType(BuiltinType::Overload))) || // We have the following case: @@ -166,13 +168,13 @@ struct SatisfactionStackRAII { const llvm::FoldingSetNodeID &FSNID) : SemaRef(SemaRef) { if (ND) { - SemaRef.PushSatisfactionStackEntry(ND, FSNID); - Inserted = true; + SemaRef.Concept().PushSatisfactionStackEntry(ND, FSNID); + Inserted = true; } } ~SatisfactionStackRAII() { if (Inserted) - SemaRef.PopSatisfactionStackEntry(); + SemaRef.Concept().PopSatisfactionStackEntry(); } }; } // namespace @@ -323,7 +325,7 @@ DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID, // expression, or when trying to determine the constexpr-ness of special // members. Otherwise we could just use the // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function. - if (S.SatisfactionStackContains(Templ, ID)) { + if (S.Concept().SatisfactionStackContains(Templ, ID)) { S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self) << const_cast(E) << E->getSourceRange(); return true; @@ -401,7 +403,7 @@ static ExprResult calculateConstraintSatisfaction( } } - if (!S.CheckConstraintExpression(SubstitutedExpression.get())) + if (!S.Concept().CheckConstraintExpression(SubstitutedExpression.get())) return ExprError(); // [temp.constr.atomic]p3: To determine if an atomic constraint is @@ -472,7 +474,7 @@ static bool CheckConstraintSatisfaction( return false; } -bool Sema::CheckConstraintSatisfaction( +bool SemaConcept::CheckConstraintSatisfaction( const NamedDecl *Template, ArrayRef ConstraintExprs, llvm::SmallVectorImpl &ConvertedConstraints, const MultiLevelTemplateArgumentList &TemplateArgsLists, @@ -483,7 +485,7 @@ bool Sema::CheckConstraintSatisfaction( } if (!Template) { return ::CheckConstraintSatisfaction( - *this, nullptr, ConstraintExprs, ConvertedConstraints, + SemaRef, nullptr, ConstraintExprs, ConvertedConstraints, TemplateArgsLists, TemplateIDRange, OutSatisfaction); } @@ -497,7 +499,7 @@ bool Sema::CheckConstraintSatisfaction( List.Args.end()); llvm::FoldingSetNodeID ID; - ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs); + ConstraintSatisfaction::Profile(ID, SemaRef.Context, Template, FlattenedArgs); void *InsertPos; if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) { OutSatisfaction = *Cached; @@ -506,7 +508,7 @@ bool Sema::CheckConstraintSatisfaction( auto Satisfaction = std::make_unique(Template, FlattenedArgs); - if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, + if (::CheckConstraintSatisfaction(SemaRef, Template, ConstraintExprs, ConvertedConstraints, TemplateArgsLists, TemplateIDRange, *Satisfaction)) { OutSatisfaction = *Satisfaction; @@ -535,63 +537,26 @@ bool Sema::CheckConstraintSatisfaction( return false; } -bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, - ConstraintSatisfaction &Satisfaction) { +bool SemaConcept::CheckConstraintSatisfaction( + const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { return calculateConstraintSatisfaction( - *this, ConstraintExpr, Satisfaction, + SemaRef, ConstraintExpr, Satisfaction, [this](const Expr *AtomicExpr) -> ExprResult { // We only do this to immitate lvalue-to-rvalue conversion. - return PerformContextuallyConvertToBool( + return SemaRef.PerformContextuallyConvertToBool( const_cast(AtomicExpr)); }) .isInvalid(); } -bool Sema::addInstantiatedCapturesToScope( - FunctionDecl *Function, const FunctionDecl *PatternDecl, - LocalInstantiationScope &Scope, - const MultiLevelTemplateArgumentList &TemplateArgs) { - const auto *LambdaClass = cast(Function)->getParent(); - const auto *LambdaPattern = cast(PatternDecl)->getParent(); - - unsigned Instantiated = 0; - - auto AddSingleCapture = [&](const ValueDecl *CapturedPattern, - unsigned Index) { - ValueDecl *CapturedVar = LambdaClass->getCapture(Index)->getCapturedVar(); - if (CapturedVar->isInitCapture()) - Scope.InstantiatedLocal(CapturedPattern, CapturedVar); - }; - - for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) { - if (!CapturePattern.capturesVariable()) { - Instantiated++; - continue; - } - const ValueDecl *CapturedPattern = CapturePattern.getCapturedVar(); - if (!CapturedPattern->isParameterPack()) { - AddSingleCapture(CapturedPattern, Instantiated++); - } else { - Scope.MakeInstantiatedLocalArgPack(CapturedPattern); - std::optional NumArgumentsInExpansion = - getNumArgumentsInExpansion(CapturedPattern->getType(), TemplateArgs); - if (!NumArgumentsInExpansion) - continue; - for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) - AddSingleCapture(CapturedPattern, Instantiated++); - } - } - return false; -} - -bool Sema::SetupConstraintScope( +bool SemaConcept::SetupConstraintScope( FunctionDecl *FD, std::optional> TemplateArgs, const MultiLevelTemplateArgumentList &MLTAL, LocalInstantiationScope &Scope) { if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) { FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate(); - InstantiatingTemplate Inst( - *this, FD->getPointOfInstantiation(), + Sema::InstantiatingTemplate Inst( + SemaRef, FD->getPointOfInstantiation(), Sema::InstantiatingTemplate::ConstraintsCheck{}, PrimaryTemplate, TemplateArgs ? *TemplateArgs : ArrayRef{}, SourceRange()); @@ -606,7 +571,7 @@ bool Sema::SetupConstraintScope( if (auto *SpecArgs = FD->getTemplateSpecializationArgs()) { MultiLevelTemplateArgumentList JustTemplArgs(FD, SpecArgs->asArray(), /*Final=*/false); - if (addInstantiatedParametersToScope( + if (SemaRef.addInstantiatedParametersToScope( FD, PrimaryTemplate->getTemplatedDecl(), Scope, JustTemplArgs)) return true; } @@ -621,8 +586,8 @@ bool Sema::SetupConstraintScope( PrimaryTemplate->getInstantiatedFromMemberTemplate()) { while (FromMemTempl->getInstantiatedFromMemberTemplate()) FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate(); - if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(), - Scope, MLTAL)) + if (SemaRef.addInstantiatedParametersToScope( + FD, FromMemTempl->getTemplatedDecl(), Scope, MLTAL)) return true; } @@ -636,8 +601,8 @@ bool Sema::SetupConstraintScope( ? FD->getInstantiatedFromMemberFunction() : FD->getInstantiatedFromDecl(); - InstantiatingTemplate Inst( - *this, FD->getPointOfInstantiation(), + Sema::InstantiatingTemplate Inst( + SemaRef, FD->getPointOfInstantiation(), Sema::InstantiatingTemplate::ConstraintsCheck{}, InstantiatedFrom, TemplateArgs ? *TemplateArgs : ArrayRef{}, SourceRange()); @@ -646,40 +611,45 @@ bool Sema::SetupConstraintScope( // Case where this was not a template, but instantiated as a // child-function. - if (addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, MLTAL)) + if (SemaRef.addInstantiatedParametersToScope(FD, InstantiatedFrom, Scope, + MLTAL)) return true; } return false; } -// This function collects all of the template arguments for the purposes of -// constraint-instantiation and checking. -std::optional -Sema::SetupConstraintCheckingTemplateArgumentsAndScope( - FunctionDecl *FD, std::optional> TemplateArgs, +/// This function collects all of the template arguments for the purposes of +/// constraint-instantiation and checking. +/// Used during constraint checking, sets up the constraint template argument +/// lists, and calls SetupConstraintScope to set up the +/// LocalInstantiationScope to have the proper set of ParVarDecls configured. +static std::optional +SetupConstraintCheckingTemplateArgumentsAndScope( + Sema &SemaRef, FunctionDecl *FD, + std::optional> TemplateArgs, LocalInstantiationScope &Scope) { MultiLevelTemplateArgumentList MLTAL; // Collect the list of template arguments relative to the 'primary' template. // We need the entire list, since the constraint is completely uninstantiated // at this point. - MLTAL = - getTemplateInstantiationArgs(FD, FD->getLexicalDeclContext(), - /*Final=*/false, /*Innermost=*/std::nullopt, - /*RelativeToPrimary=*/true, - /*Pattern=*/nullptr, - /*ForConstraintInstantiation=*/true); - if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope)) + MLTAL = SemaRef.getTemplateInstantiationArgs( + FD, FD->getLexicalDeclContext(), + /*Final=*/false, /*Innermost=*/std::nullopt, + /*RelativeToPrimary=*/true, + /*Pattern=*/nullptr, + /*ForConstraintInstantiation=*/true); + if (SemaRef.Concept().SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope)) return std::nullopt; return MLTAL; } -bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, - ConstraintSatisfaction &Satisfaction, - SourceLocation UsageLoc, - bool ForOverloadResolution) { +bool SemaConcept::CheckFunctionConstraints(const FunctionDecl *FD, + ConstraintSatisfaction &Satisfaction, + SourceLocation UsageLoc, + bool ForOverloadResolution) { // Don't check constraints if the function is dependent. Also don't check if // this is a function template specialization, as the call to // CheckinstantiatedFunctionTemplateConstraints after this will check it @@ -713,11 +683,11 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, CtxToSave = CtxToSave->getNonTransparentContext(); } - ContextRAII SavedContext{*this, CtxToSave}; - LocalInstantiationScope Scope(*this, !ForOverloadResolution); + Sema::ContextRAII SavedContext{SemaRef, CtxToSave}; + LocalInstantiationScope Scope(SemaRef, !ForOverloadResolution); std::optional MLTAL = SetupConstraintCheckingTemplateArgumentsAndScope( - const_cast(FD), {}, Scope); + SemaRef, const_cast(FD), {}, Scope); if (!MLTAL) return true; @@ -728,10 +698,11 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, ThisQuals = Method->getMethodQualifiers(); Record = const_cast(Method->getParent()); } - CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); + Sema::CXXThisScopeRAII ThisScope(SemaRef, Record, ThisQuals, + Record != nullptr); - LambdaScopeForCallOperatorInstantiationRAII LambdaScope( - *this, const_cast(FD), *MLTAL, Scope, + Sema::LambdaScopeForCallOperatorInstantiationRAII LambdaScope( + SemaRef, const_cast(FD), *MLTAL, Scope, ForOverloadResolution); return CheckConstraintSatisfaction( @@ -740,7 +711,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. @@ -785,7 +755,7 @@ namespace { } // namespace static const Expr *SubstituteConstraintExpressionWithoutSatisfaction( - Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo, + Sema &S, const TemplateCompareNewDeclInfo &DeclInfo, const Expr *ConstrExpr) { MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( DeclInfo.getDecl(), DeclInfo.getLexicalDeclContext(), /*Final=*/false, @@ -837,23 +807,22 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction( return SubstConstr.get(); } -bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old, - const Expr *OldConstr, - const TemplateCompareNewDeclInfo &New, - const Expr *NewConstr) { +bool SemaConcept::AreConstraintExpressionsEqual( + const NamedDecl *Old, const Expr *OldConstr, + const TemplateCompareNewDeclInfo &New, const Expr *NewConstr) { if (OldConstr == NewConstr) return true; // C++ [temp.constr.decl]p4 if (Old && !New.isInvalid() && !New.ContainsDecl(Old) && Old->getLexicalDeclContext() != New.getLexicalDeclContext()) { if (const Expr *SubstConstr = - SubstituteConstraintExpressionWithoutSatisfaction(*this, Old, + SubstituteConstraintExpressionWithoutSatisfaction(SemaRef, Old, OldConstr)) OldConstr = SubstConstr; else return false; if (const Expr *SubstConstr = - SubstituteConstraintExpressionWithoutSatisfaction(*this, New, + SubstituteConstraintExpressionWithoutSatisfaction(SemaRef, New, NewConstr)) NewConstr = SubstConstr; else @@ -861,12 +830,13 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old, } llvm::FoldingSetNodeID ID1, ID2; - OldConstr->Profile(ID1, Context, /*Canonical=*/true); - NewConstr->Profile(ID2, Context, /*Canonical=*/true); + OldConstr->Profile(ID1, SemaRef.Context, /*Canonical=*/true); + NewConstr->Profile(ID2, SemaRef.Context, /*Canonical=*/true); return ID1 == ID2; } -bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) { +bool SemaConcept::FriendConstraintsDependOnEnclosingTemplate( + const FunctionDecl *FD) { assert(FD->getFriendObjectKind() && "Must be a friend!"); // The logic for non-templates is handled in ASTContext::isSameEntity, so we @@ -878,16 +848,16 @@ bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) { SmallVector ACs; FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs); - unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD); + unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(SemaRef, FD); for (const Expr *Constraint : ACs) - if (ConstraintExpressionDependsOnEnclosingTemplate(FD, OldTemplateDepth, - Constraint)) + if (SemaRef.ConstraintExpressionDependsOnEnclosingTemplate( + FD, OldTemplateDepth, Constraint)) return true; return false; } -bool Sema::EnsureTemplateArgumentListConstraints( +bool SemaConcept::EnsureTemplateArgumentListConstraints( TemplateDecl *TD, const MultiLevelTemplateArgumentList &TemplateArgsLists, SourceRange TemplateIDRange) { ConstraintSatisfaction Satisfaction; @@ -900,21 +870,21 @@ bool Sema::EnsureTemplateArgumentListConstraints( if (!Satisfaction.IsSatisfied) { SmallString<128> TemplateArgString; TemplateArgString = " "; - TemplateArgString += getTemplateArgumentBindingsText( + TemplateArgString += SemaRef.getTemplateArgumentBindingsText( TD->getTemplateParameters(), TemplateArgsLists.getInnermost().data(), TemplateArgsLists.getInnermost().size()); Diag(TemplateIDRange.getBegin(), diag::err_template_arg_list_constraints_not_satisfied) - << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << TD - << TemplateArgString << TemplateIDRange; + << (int)SemaRef.getTemplateNameKindForDiagnostics(TemplateName(TD)) + << TD << TemplateArgString << TemplateIDRange; DiagnoseUnsatisfiedConstraint(Satisfaction); return true; } return false; } -bool Sema::CheckInstantiatedFunctionTemplateConstraints( +bool SemaConcept::CheckInstantiatedFunctionTemplateConstraints( SourceLocation PointOfInstantiation, FunctionDecl *Decl, ArrayRef TemplateArgs, ConstraintSatisfaction &Satisfaction) { @@ -931,12 +901,12 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( // Enter the scope of this instantiation. We don't use // PushDeclContext because we don't have a scope. - Sema::ContextRAII savedContext(*this, Decl); - LocalInstantiationScope Scope(*this); + Sema::ContextRAII savedContext(SemaRef, Decl); + LocalInstantiationScope Scope(SemaRef); std::optional MLTAL = - SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs, - Scope); + SetupConstraintCheckingTemplateArgumentsAndScope(SemaRef, Decl, + TemplateArgs, Scope); if (!MLTAL) return true; @@ -948,9 +918,10 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( Record = Method->getParent(); } - CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); - LambdaScopeForCallOperatorInstantiationRAII LambdaScope( - *this, const_cast(Decl), *MLTAL, Scope); + Sema::CXXThisScopeRAII ThisScope(SemaRef, Record, ThisQuals, + Record != nullptr); + Sema::LambdaScopeForCallOperatorInstantiationRAII LambdaScope( + SemaRef, const_cast(Decl), *MLTAL, Scope); llvm::SmallVector Converted; return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, @@ -1014,7 +985,8 @@ static void diagnoseUnsatisfiedRequirement(Sema &S, diag::note_expr_requirement_constraints_not_satisfied) << (int)First << ConstraintExpr; } - S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction()); + S.Concept().DiagnoseUnsatisfiedConstraint( + ConstraintExpr->getSatisfaction()); break; } case concepts::ExprRequirement::SS_Satisfied: @@ -1146,7 +1118,7 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, diag::note_concept_specialization_constraint_evaluated_to_false) << (int)First << CSE; } - S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); + S.Concept().DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); return; } else if (auto *RE = dyn_cast(SubstExpr)) { // FIXME: RequiresExpr should store dependent diagnostics. @@ -1191,30 +1163,27 @@ static void diagnoseUnsatisfiedConstraintExpr( Record.template get(), First); } -void -Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction, - bool First) { +void SemaConcept::DiagnoseUnsatisfiedConstraint( + const ConstraintSatisfaction &Satisfaction, bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); for (auto &Pair : Satisfaction.Details) { - diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); + diagnoseUnsatisfiedConstraintExpr(SemaRef, Pair.first, Pair.second, First); First = false; } } -void Sema::DiagnoseUnsatisfiedConstraint( - const ASTConstraintSatisfaction &Satisfaction, - bool First) { +void SemaConcept::DiagnoseUnsatisfiedConstraint( + const ASTConstraintSatisfaction &Satisfaction, bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); for (auto &Pair : Satisfaction) { - diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); + diagnoseUnsatisfiedConstraintExpr(SemaRef, Pair.first, Pair.second, First); First = false; } } -const NormalizedConstraint * -Sema::getNormalizedAssociatedConstraints( +const NormalizedConstraint *SemaConcept::getNormalizedAssociatedConstraints( NamedDecl *ConstrainedDecl, ArrayRef AssociatedConstraints) { // In case the ConstrainedDecl comes from modules, it is necessary to use // the canonical decl to avoid different atomic constraints with the 'same' @@ -1223,15 +1192,14 @@ Sema::getNormalizedAssociatedConstraints( auto CacheEntry = NormalizationCache.find(ConstrainedDecl); if (CacheEntry == NormalizationCache.end()) { - auto Normalized = - NormalizedConstraint::fromConstraintExprs(*this, ConstrainedDecl, - AssociatedConstraints); + auto Normalized = NormalizedConstraint::fromConstraintExprs( + SemaRef, ConstrainedDecl, AssociatedConstraints); CacheEntry = NormalizationCache .try_emplace(ConstrainedDecl, Normalized - ? new (Context) NormalizedConstraint( - std::move(*Normalized)) + ? new (SemaRef.Context) + NormalizedConstraint(std::move(*Normalized)) : nullptr) .first; } @@ -1377,8 +1345,8 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, NamedDecl *D, const Expr *E) { // expression, the program is ill-formed; no diagnostic is required. // [...] ConceptDecl *CD = CSE->getNamedConcept(); - SubNF = S.getNormalizedAssociatedConstraints(CD, - {CD->getConstraintExpr()}); + SubNF = S.Concept().getNormalizedAssociatedConstraints( + CD, {CD->getConstraintExpr()}); if (!SubNF) return std::nullopt; } @@ -1496,12 +1464,12 @@ static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef P, // In order to determine if a constraint P subsumes a constraint Q, P is // transformed into disjunctive normal form, and Q is transformed into // conjunctive normal form. [...] - auto *PNormalized = S.getNormalizedAssociatedConstraints(DP, P); + auto *PNormalized = S.Concept().getNormalizedAssociatedConstraints(DP, P); if (!PNormalized) return true; const NormalForm PDNF = makeDNF(*PNormalized); - auto *QNormalized = S.getNormalizedAssociatedConstraints(DQ, Q); + auto *QNormalized = S.Concept().getNormalizedAssociatedConstraints(DQ, Q); if (!QNormalized) return true; const NormalForm QCNF = makeCNF(*QNormalized); @@ -1510,11 +1478,11 @@ static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef P, return false; } -bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, - MutableArrayRef AC1, - NamedDecl *D2, - MutableArrayRef AC2, - bool &Result) { +bool SemaConcept::IsAtLeastAsConstrained(NamedDecl *D1, + MutableArrayRef AC1, + NamedDecl *D2, + MutableArrayRef AC2, + bool &Result) { if (const auto *FD1 = dyn_cast(D1)) { auto IsExpectedEntity = [](const FunctionDecl *FD) { FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind(); @@ -1547,69 +1515,70 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, return false; } - unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true); - unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true); + unsigned Depth1 = CalculateTemplateDepthForConstraints(SemaRef, D1, true); + unsigned Depth2 = CalculateTemplateDepthForConstraints(SemaRef, D2, true); for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) { if (Depth2 > Depth1) { - AC1[I] = AdjustConstraintDepth(*this, Depth2 - Depth1) + AC1[I] = AdjustConstraintDepth(SemaRef, Depth2 - Depth1) .TransformExpr(const_cast(AC1[I])) .get(); } else if (Depth1 > Depth2) { - AC2[I] = AdjustConstraintDepth(*this, Depth1 - Depth2) + AC2[I] = AdjustConstraintDepth(SemaRef, Depth1 - Depth2) .TransformExpr(const_cast(AC2[I])) .get(); } } - if (subsumes(*this, D1, AC1, D2, AC2, Result, - [this] (const AtomicConstraint &A, const AtomicConstraint &B) { - return A.subsumes(Context, B); - })) + if (subsumes(SemaRef, D1, AC1, D2, AC2, Result, + [this](const AtomicConstraint &A, const AtomicConstraint &B) { + return A.subsumes(SemaRef.Context, B); + })) return true; SubsumptionCache.try_emplace(Key, Result); return false; } -bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, - ArrayRef AC1, NamedDecl *D2, ArrayRef AC2) { - if (isSFINAEContext()) +bool SemaConcept::MaybeEmitAmbiguousAtomicConstraintsDiagnostic( + NamedDecl *D1, ArrayRef AC1, NamedDecl *D2, + ArrayRef AC2) { + if (SemaRef.isSFINAEContext()) // No need to work here because our notes would be discarded. return false; if (AC1.empty() || AC2.empty()) return false; - auto NormalExprEvaluator = - [this] (const AtomicConstraint &A, const AtomicConstraint &B) { - return A.subsumes(Context, B); - }; + auto NormalExprEvaluator = [this](const AtomicConstraint &A, + const AtomicConstraint &B) { + return A.subsumes(SemaRef.Context, B); + }; const Expr *AmbiguousAtomic1 = nullptr, *AmbiguousAtomic2 = nullptr; - auto IdenticalExprEvaluator = - [&] (const AtomicConstraint &A, const AtomicConstraint &B) { - if (!A.hasMatchingParameterMapping(Context, B)) - return false; - const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr; - if (EA == EB) - return true; + auto IdenticalExprEvaluator = [&](const AtomicConstraint &A, + const AtomicConstraint &B) { + if (!A.hasMatchingParameterMapping(SemaRef.Context, B)) + return false; + const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr; + if (EA == EB) + return true; - // Not the same source level expression - are the expressions - // identical? - llvm::FoldingSetNodeID IDA, IDB; - EA->Profile(IDA, Context, /*Canonical=*/true); - EB->Profile(IDB, Context, /*Canonical=*/true); - if (IDA != IDB) - return false; + // Not the same source level expression - are the expressions + // identical? + llvm::FoldingSetNodeID IDA, IDB; + EA->Profile(IDA, SemaRef.Context, /*Canonical=*/true); + EB->Profile(IDB, SemaRef.Context, /*Canonical=*/true); + if (IDA != IDB) + return false; - AmbiguousAtomic1 = EA; - AmbiguousAtomic2 = EB; - return true; - }; + AmbiguousAtomic1 = EA; + AmbiguousAtomic2 = EB; + return true; + }; { // The subsumption checks might cause diagnostics - SFINAETrap Trap(*this); + Sema::SFINAETrap Trap(SemaRef); auto *Normalized1 = getNormalizedAssociatedConstraints(D1, AC1); if (!Normalized1) return false; @@ -1700,3 +1669,672 @@ concepts::TypeRequirement::TypeRequirement(TypeSourceInfo *T) : Value(T), Status(T->getType()->isInstantiationDependentType() ? SS_Dependent : SS_Satisfied) {} + +RequiresExprBodyDecl * +SemaConcept::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, + ArrayRef LocalParameters, + Scope *BodyScope) { + assert(BodyScope); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create( + SemaRef.Context, SemaRef.CurContext, RequiresKWLoc); + + SemaRef.PushDeclContext(BodyScope, Body); + + for (ParmVarDecl *Param : LocalParameters) { + if (Param->hasDefaultArg()) + // C++2a [expr.prim.req] p4 + // [...] A local parameter of a requires-expression shall not have a + // default argument. [...] + Diag(Param->getDefaultArgRange().getBegin(), + diag::err_requires_expr_local_parameter_default_argument); + // Ignore default argument and move on + + Param->setDeclContext(Body); + // If this has an identifier, add it to the scope stack. + if (Param->getIdentifier()) { + SemaRef.CheckShadow(BodyScope, Param); + SemaRef.PushOnScopeChains(Param, BodyScope); + } + } + return Body; +} + +void SemaConcept::ActOnFinishRequiresExpr() { + assert(SemaRef.CurContext && "DeclContext imbalance!"); + SemaRef.CurContext = SemaRef.CurContext->getLexicalParent(); + assert(SemaRef.CurContext && "Popped translation unit!"); +} + +concepts::Requirement *SemaConcept::ActOnSimpleRequirement(Expr *E) { + return BuildExprRequirement(E, /*IsSimple=*/true, + /*NoexceptLoc=*/SourceLocation(), + /*ReturnTypeRequirement=*/{}); +} + +concepts::Requirement *SemaConcept::ActOnTypeRequirement( + SourceLocation TypenameKWLoc, CXXScopeSpec &SS, SourceLocation NameLoc, + const IdentifierInfo *TypeName, TemplateIdAnnotation *TemplateId) { + assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) && + "Exactly one of TypeName and TemplateId must be specified."); + TypeSourceInfo *TSI = nullptr; + if (TypeName) { + QualType T = SemaRef.CheckTypenameType( + ElaboratedTypeKeyword::Typename, TypenameKWLoc, + SS.getWithLocInContext(SemaRef.Context), *TypeName, NameLoc, &TSI, + /*DeducedTSTContext=*/false); + if (T.isNull()) + return nullptr; + } else { + ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + TypeResult T = SemaRef.ActOnTypenameType( + SemaRef.getCurScope(), TypenameKWLoc, SS, TemplateId->TemplateKWLoc, + TemplateId->Template, TemplateId->Name, TemplateId->TemplateNameLoc, + TemplateId->LAngleLoc, ArgsPtr, TemplateId->RAngleLoc); + if (T.isInvalid()) + return nullptr; + if (SemaRef.GetTypeFromParser(T.get(), &TSI).isNull()) + return nullptr; + } + return BuildTypeRequirement(TSI); +} + +concepts::Requirement * +SemaConcept::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc) { + return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, + /*ReturnTypeRequirement=*/{}); +} + +concepts::Requirement *SemaConcept::ActOnCompoundRequirement( + Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, unsigned Depth) { + // C++2a [expr.prim.req.compound] p1.3.3 + // [..] the expression is deduced against an invented function template + // F [...] F is a void function template with a single type template + // parameter T declared with the constrained-parameter. Form a new + // cv-qualifier-seq cv by taking the union of const and volatile specifiers + // around the constrained-parameter. F has a single parameter whose + // type-specifier is cv T followed by the abstract-declarator. [...] + // + // The cv part is done in the calling function - we get the concept with + // arguments and the abstract declarator with the correct CV qualification and + // have to synthesize T and the single parameter of F. + auto &II = SemaRef.Context.Idents.get("expr-type"); + auto *TParam = + TemplateTypeParmDecl::Create(SemaRef.Context, SemaRef.CurContext, + SourceLocation(), SourceLocation(), Depth, + /*Index=*/0, &II, + /*Typename=*/true, + /*ParameterPack=*/false, + /*HasTypeConstraint=*/true); + + if (BuildTypeConstraint(SS, TypeConstraint, TParam, + /*EllipsisLoc=*/SourceLocation(), + /*AllowUnexpandedPack=*/true)) + // Just produce a requirement with no type requirements. + return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {}); + + auto *TPL = TemplateParameterList::Create( + SemaRef.Context, SourceLocation(), SourceLocation(), + ArrayRef(TParam), SourceLocation(), + /*RequiresClause=*/nullptr); + return BuildExprRequirement( + E, /*IsSimple=*/false, NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement(TPL)); +} + +concepts::ExprRequirement *SemaConcept::BuildExprRequirement( + Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { + auto Status = concepts::ExprRequirement::SS_Satisfied; + ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr; + if (E->isInstantiationDependent() || E->getType()->isPlaceholderType() || + ReturnTypeRequirement.isDependent()) + Status = concepts::ExprRequirement::SS_Dependent; + else if (NoexceptLoc.isValid() && + SemaRef.canThrow(E) == CanThrowResult::CT_Can) + Status = concepts::ExprRequirement::SS_NoexceptNotMet; + else if (ReturnTypeRequirement.isSubstitutionFailure()) + Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure; + else if (ReturnTypeRequirement.isTypeConstraint()) { + // C++2a [expr.prim.req]p1.3.3 + // The immediately-declared constraint ([temp]) of decltype((E)) shall + // be satisfied. + TemplateParameterList *TPL = + ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); + QualType MatchedType = + SemaRef.Context.getReferenceQualifiedType(E).getCanonicalType(); + llvm::SmallVector Args; + Args.push_back(TemplateArgument(MatchedType)); + + auto *Param = cast(TPL->getParam(0)); + + MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false); + MLTAL.addOuterRetainedLevels(TPL->getDepth()); + const TypeConstraint *TC = Param->getTypeConstraint(); + assert(TC && "Type Constraint cannot be null here"); + auto *IDC = TC->getImmediatelyDeclaredConstraint(); + assert(IDC && "ImmediatelyDeclaredConstraint can't be null here."); + ExprResult Constraint = SemaRef.SubstExpr(IDC, MLTAL); + if (Constraint.isInvalid()) { + return new (SemaRef.Context) concepts::ExprRequirement( + concepts::createSubstDiagAt(SemaRef, IDC->getExprLoc(), + [&](llvm::raw_ostream &OS) { + IDC->printPretty( + OS, /*Helper=*/nullptr, + SemaRef.getPrintingPolicy()); + }), + IsSimple, NoexceptLoc, ReturnTypeRequirement); + } + SubstitutedConstraintExpr = + cast(Constraint.get()); + if (!SubstitutedConstraintExpr->isSatisfied()) + Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied; + } + return new (SemaRef.Context) + concepts::ExprRequirement(E, IsSimple, NoexceptLoc, ReturnTypeRequirement, + Status, SubstitutedConstraintExpr); +} + +concepts::ExprRequirement *SemaConcept::BuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic, + bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { + return new (SemaRef.Context) concepts::ExprRequirement( + ExprSubstitutionDiagnostic, IsSimple, NoexceptLoc, ReturnTypeRequirement); +} + +concepts::TypeRequirement * +SemaConcept::BuildTypeRequirement(TypeSourceInfo *Type) { + return new (SemaRef.Context) concepts::TypeRequirement(Type); +} + +concepts::TypeRequirement *SemaConcept::BuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (SemaRef.Context) concepts::TypeRequirement(SubstDiag); +} + +concepts::Requirement *SemaConcept::ActOnNestedRequirement(Expr *Constraint) { + return BuildNestedRequirement(Constraint); +} + +concepts::NestedRequirement * +SemaConcept::BuildNestedRequirement(Expr *Constraint) { + ConstraintSatisfaction Satisfaction; + if (!Constraint->isInstantiationDependent() && + CheckConstraintSatisfaction(nullptr, {Constraint}, /*TemplateArgs=*/{}, + Constraint->getSourceRange(), Satisfaction)) + return nullptr; + return new (SemaRef.Context) + concepts::NestedRequirement(SemaRef.Context, Constraint, Satisfaction); +} + +concepts::NestedRequirement *SemaConcept::BuildNestedRequirement( + StringRef InvalidConstraintEntity, + const ASTConstraintSatisfaction &Satisfaction) { + return new (SemaRef.Context) concepts::NestedRequirement( + InvalidConstraintEntity, + ASTConstraintSatisfaction::Rebuild(SemaRef.Context, Satisfaction)); +} + +ExprResult SemaConcept::ActOnRequiresExpr( + SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, + SourceLocation LParenLoc, ArrayRef LocalParameters, + SourceLocation RParenLoc, ArrayRef Requirements, + SourceLocation ClosingBraceLoc) { + auto *RE = RequiresExpr::Create(SemaRef.Context, RequiresKWLoc, Body, + LParenLoc, LocalParameters, RParenLoc, + Requirements, ClosingBraceLoc); + if (SemaRef.DiagnoseUnexpandedParameterPackInRequiresExpr(RE)) + return ExprError(); + return RE; +} + +bool SemaConcept::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) { + + TemplateName TN = TypeConstr->Template.get(); + ConceptDecl *CD = cast(TN.getAsTemplateDecl()); + + // C++2a [temp.param]p4: + // [...] The concept designated by a type-constraint shall be a type + // concept ([temp.concept]). + if (!CD->isTypeConcept()) { + Diag(TypeConstr->TemplateNameLoc, + diag::err_type_constraint_non_type_concept); + return true; + } + + bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid(); + + if (!WereArgsSpecified && + CD->getTemplateParameters()->getMinRequiredArguments() > 1) { + Diag(TypeConstr->TemplateNameLoc, + diag::err_type_constraint_missing_arguments) + << CD; + return true; + } + return false; +} + +bool SemaConcept::ActOnTypeConstraint( + const CXXScopeSpec &SS, TemplateIdAnnotation *TypeConstr, + TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc) { + return BuildTypeConstraint(SS, TypeConstr, ConstrainedParameter, EllipsisLoc, + false); +} + +bool SemaConcept::BuildTypeConstraint( + const CXXScopeSpec &SS, TemplateIdAnnotation *TypeConstr, + TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc, + bool AllowUnexpandedPack) { + + if (CheckTypeConstraint(TypeConstr)) + return true; + + TemplateName TN = TypeConstr->Template.get(); + ConceptDecl *CD = cast(TN.getAsTemplateDecl()); + UsingShadowDecl *USD = TN.getAsUsingShadowDecl(); + + DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name), + TypeConstr->TemplateNameLoc); + + TemplateArgumentListInfo TemplateArgs; + if (TypeConstr->LAngleLoc.isValid()) { + TemplateArgs = SemaRef.makeTemplateArgumentListInfo(*TypeConstr); + + if (EllipsisLoc.isInvalid() && !AllowUnexpandedPack) { + for (TemplateArgumentLoc Arg : TemplateArgs.arguments()) { + if (SemaRef.DiagnoseUnexpandedParameterPack(Arg, + Sema::UPPC_TypeConstraint)) + return true; + } + } + } + return AttachTypeConstraint( + SS.isSet() ? SS.getWithLocInContext(SemaRef.Context) + : NestedNameSpecifierLoc(), + ConceptName, CD, /*FoundDecl=*/USD ? cast(USD) : CD, + TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, + ConstrainedParameter, EllipsisLoc); +} + +template +static ExprResult formImmediatelyDeclaredConstraint( + Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc, + SourceLocation RAngleLoc, QualType ConstrainedType, + SourceLocation ParamNameLoc, ArgumentLocAppender Appender, + SourceLocation EllipsisLoc) { + + TemplateArgumentListInfo ConstraintArgs; + ConstraintArgs.addArgument( + S.getTrivialTemplateArgumentLoc(TemplateArgument(ConstrainedType), + /*NTTPType=*/QualType(), ParamNameLoc)); + + ConstraintArgs.setRAngleLoc(RAngleLoc); + ConstraintArgs.setLAngleLoc(LAngleLoc); + Appender(ConstraintArgs); + + // C++2a [temp.param]p4: + // [...] This constraint-expression E is called the immediately-declared + // constraint of T. [...] + CXXScopeSpec SS; + SS.Adopt(NS); + ExprResult ImmediatelyDeclaredConstraint = S.Concept().CheckConceptTemplateId( + SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, + /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, NamedConcept, + &ConstraintArgs); + if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid()) + return ImmediatelyDeclaredConstraint; + + // C++2a [temp.param]p4: + // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). + // + // We have the following case: + // + // template concept C1 = true; + // template struct s1; + // + // The constraint: (C1 && ...) + // + // Note that the type of C1 is known to be 'bool', so we don't need to do + // any unqualified lookups for 'operator&&' here. + return S.BuildCXXFoldExpr(/*UnqualifiedLookup=*/nullptr, + /*LParenLoc=*/SourceLocation(), + ImmediatelyDeclaredConstraint.get(), BO_LAnd, + EllipsisLoc, /*RHS=*/nullptr, + /*RParenLoc=*/SourceLocation(), + /*NumExpansions=*/std::nullopt); +} + +/// Attach a type-constraint to a template parameter. +/// \returns true if an error occurred. This can happen if the +/// immediately-declared constraint could not be formed (e.g. incorrect number +/// of arguments for the named concept). +bool SemaConcept::AttachTypeConstraint( + NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, NamedDecl *FoundDecl, + const TemplateArgumentListInfo *TemplateArgs, + TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc) { + // C++2a [temp.param]p4: + // [...] If Q is of the form C, then let E' be + // C. Otherwise, let E' be C. [...] + const ASTTemplateArgumentListInfo *ArgsAsWritten = + TemplateArgs + ? ASTTemplateArgumentListInfo::Create(SemaRef.Context, *TemplateArgs) + : nullptr; + + QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0); + + ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint( + SemaRef, NS, NameInfo, NamedConcept, FoundDecl, + TemplateArgs ? TemplateArgs->getLAngleLoc() : SourceLocation(), + TemplateArgs ? TemplateArgs->getRAngleLoc() : SourceLocation(), + ParamAsArgument, ConstrainedParameter->getLocation(), + [&](TemplateArgumentListInfo &ConstraintArgs) { + if (TemplateArgs) + for (const auto &ArgLoc : TemplateArgs->arguments()) + ConstraintArgs.addArgument(ArgLoc); + }, + EllipsisLoc); + if (ImmediatelyDeclaredConstraint.isInvalid()) + return true; + + auto *CL = ConceptReference::Create(SemaRef.Context, /*NNS=*/NS, + /*TemplateKWLoc=*/SourceLocation{}, + /*ConceptNameInfo=*/NameInfo, + /*FoundDecl=*/FoundDecl, + /*NamedConcept=*/NamedConcept, + /*ArgsWritten=*/ArgsAsWritten); + ConstrainedParameter->setTypeConstraint(CL, + ImmediatelyDeclaredConstraint.get()); + return false; +} + +bool SemaConcept::AttachTypeConstraint( + AutoTypeLoc TL, NonTypeTemplateParmDecl *NewConstrainedParm, + NonTypeTemplateParmDecl *OrigConstrainedParm, SourceLocation EllipsisLoc) { + if (NewConstrainedParm->getType() != TL.getType() || + TL.getAutoKeyword() != AutoTypeKeyword::Auto) { + Diag(NewConstrainedParm->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), + diag::err_unsupported_placeholder_constraint) + << NewConstrainedParm->getTypeSourceInfo() + ->getTypeLoc() + .getSourceRange(); + return true; + } + // FIXME: Concepts: This should be the type of the placeholder, but this is + // unclear in the wording right now. + DeclRefExpr *Ref = SemaRef.BuildDeclRefExpr( + OrigConstrainedParm, OrigConstrainedParm->getType(), VK_PRValue, + OrigConstrainedParm->getLocation()); + if (!Ref) + return true; + ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint( + SemaRef, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(), + TL.getNamedConcept(), /*FoundDecl=*/TL.getFoundDecl(), TL.getLAngleLoc(), + TL.getRAngleLoc(), SemaRef.BuildDecltypeType(Ref), + OrigConstrainedParm->getLocation(), + [&](TemplateArgumentListInfo &ConstraintArgs) { + for (unsigned I = 0, C = TL.getNumArgs(); I != C; ++I) + ConstraintArgs.addArgument(TL.getArgLoc(I)); + }, + EllipsisLoc); + if (ImmediatelyDeclaredConstraint.isInvalid() || + !ImmediatelyDeclaredConstraint.isUsable()) + return true; + + NewConstrainedParm->setPlaceholderTypeConstraint( + ImmediatelyDeclaredConstraint.get()); + return false; +} + +ExprResult SemaConcept::CheckConceptTemplateId( + const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs) { + assert(NamedConcept && "A concept template id without a template?"); + ASTContext &Context = getASTContext(); + + llvm::SmallVector SugaredConverted, CanonicalConverted; + if (SemaRef.CheckTemplateArgumentList( + NamedConcept, ConceptNameInfo.getLoc(), + const_cast(*TemplateArgs), + /*PartialTemplateArgs=*/false, SugaredConverted, CanonicalConverted, + /*UpdateArgsWithConversions=*/false)) + return ExprError(); + + SemaRef.DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc()); + + auto *CSD = ImplicitConceptSpecializationDecl::Create( + Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(), + CanonicalConverted); + ConstraintSatisfaction Satisfaction; + bool AreArgsDependent = + TemplateSpecializationType::anyDependentTemplateArguments( + *TemplateArgs, CanonicalConverted); + MultiLevelTemplateArgumentList MLTAL(NamedConcept, CanonicalConverted, + /*Final=*/false); + LocalInstantiationScope Scope(SemaRef); + + EnterExpressionEvaluationContext EECtx{ + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated, CSD}; + + if (!AreArgsDependent && + CheckConstraintSatisfaction( + NamedConcept, {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)); + return ConceptSpecializationExpr::Create( + Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction); +} + +Decl *SemaConcept::ActOnConceptDefinition( + Scope *S, MultiTemplateParamsArg TemplateParameterLists, + const IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr, + const ParsedAttributesView &Attrs) { + DeclContext *DC = SemaRef.CurContext; + + if (!DC->getRedeclContext()->isFileContext()) { + Diag(NameLoc, + diag::err_concept_decls_may_only_appear_in_global_namespace_scope); + return nullptr; + } + + if (TemplateParameterLists.size() > 1) { + Diag(NameLoc, diag::err_concept_extra_headers); + return nullptr; + } + + TemplateParameterList *Params = TemplateParameterLists.front(); + + if (Params->size() == 0) { + Diag(NameLoc, diag::err_concept_no_parameters); + return nullptr; + } + + // Ensure that the parameter pack, if present, is the last parameter in the + // template. + for (TemplateParameterList::const_iterator ParamIt = Params->begin(), + ParamEnd = Params->end(); + ParamIt != ParamEnd; ++ParamIt) { + Decl const *Param = *ParamIt; + if (Param->isParameterPack()) { + if (++ParamIt == ParamEnd) + break; + Diag(Param->getLocation(), + diag::err_template_param_pack_must_be_last_template_parameter); + return nullptr; + } + } + + if (SemaRef.DiagnoseUnexpandedParameterPack(ConstraintExpr)) + return nullptr; + + ConceptDecl *NewDecl = ConceptDecl::Create(SemaRef.Context, DC, NameLoc, Name, + Params, ConstraintExpr); + + if (NewDecl->hasAssociatedConstraints()) { + // C++2a [temp.concept]p4: + // A concept shall not have associated constraints. + Diag(NameLoc, diag::err_concept_no_associated_constraints); + NewDecl->setInvalidDecl(); + } + + // Check for conflicting previous declaration. + DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NameLoc); + LookupResult Previous(SemaRef, NameInfo, Sema::LookupOrdinaryName, + SemaRef.forRedeclarationInCurContext()); + SemaRef.LookupName(Previous, S); + SemaRef.FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage=*/false, + /*AllowInlineNamespace*/ false); + bool AddToScope = true; + CheckConceptRedefinition(NewDecl, Previous, AddToScope); + + SemaRef.ActOnDocumentableDecl(NewDecl); + if (AddToScope) + SemaRef.PushOnScopeChains(NewDecl, S); + + SemaRef.ProcessDeclAttributeList(S, NewDecl, Attrs); + + return NewDecl; +} + +void SemaConcept::CheckConceptRedefinition(ConceptDecl *NewDecl, + LookupResult &Previous, + bool &AddToScope) { + AddToScope = true; + + if (Previous.empty()) + return; + + auto *OldConcept = dyn_cast( + Previous.getRepresentativeDecl()->getUnderlyingDecl()); + if (!OldConcept) { + auto *Old = Previous.getRepresentativeDecl(); + Diag(NewDecl->getLocation(), diag::err_redefinition_different_kind) + << NewDecl->getDeclName(); + SemaRef.notePreviousDefinition(Old, NewDecl->getLocation()); + AddToScope = false; + return; + } + // Check if we can merge with a concept declaration. + bool IsSame = SemaRef.Context.isSameEntity(NewDecl, OldConcept); + if (!IsSame) { + Diag(NewDecl->getLocation(), diag::err_redefinition_different_concept) + << NewDecl->getDeclName(); + SemaRef.notePreviousDefinition(OldConcept, NewDecl->getLocation()); + AddToScope = false; + return; + } + if (SemaRef.hasReachableDefinition(OldConcept) && + SemaRef.IsRedefinitionInModule(NewDecl, OldConcept)) { + Diag(NewDecl->getLocation(), diag::err_redefinition) + << NewDecl->getDeclName(); + SemaRef.notePreviousDefinition(OldConcept, NewDecl->getLocation()); + AddToScope = false; + return; + } + if (!Previous.isSingleResult()) { + // FIXME: we should produce an error in case of ambig and failed lookups. + // Other decls (e.g. namespaces) also have this shortcoming. + return; + } + // We unwrap canonical decl late to check for module visibility. + SemaRef.Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl()); +} + +void SemaConcept::ActOnStartTrailingRequiresClause(Scope *S, Declarator &D) { + if (!D.isFunctionDeclarator()) + return; + auto &FTI = D.getFunctionTypeInfo(); + if (!FTI.Params) + return; + for (auto &Param : + ArrayRef(FTI.Params, FTI.NumParams)) { + auto *ParamDecl = cast(Param.Param); + if (ParamDecl->getDeclName()) + SemaRef.PushOnScopeChains(ParamDecl, S, /*AddToContext=*/false); + } +} + +ExprResult +SemaConcept::ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr) { + return ActOnRequiresClause(ConstraintExpr); +} + +ExprResult SemaConcept::ActOnRequiresClause(ExprResult ConstraintExpr) { + if (ConstraintExpr.isInvalid()) + return ExprError(); + + ConstraintExpr = SemaRef.CorrectDelayedTyposInExpr(ConstraintExpr); + if (ConstraintExpr.isInvalid()) + return ExprError(); + + if (SemaRef.DiagnoseUnexpandedParameterPack(ConstraintExpr.get(), + Sema::UPPC_RequiresClause)) + return ExprError(); + + return ConstraintExpr; +} + +void SemaConcept::CheckConstrainedAuto(const AutoType *AutoT, + SourceLocation Loc) { + if (ConceptDecl *Decl = AutoT->getTypeConstraintConcept()) { + SemaRef.DiagnoseUseOfDecl(Decl, Loc); + } +} + +FunctionDecl *SemaConcept::getMoreConstrainedFunction(FunctionDecl *FD1, + FunctionDecl *FD2) { + assert(!FD1->getDescribedTemplate() && !FD2->getDescribedTemplate() && + "not for function templates"); + FunctionDecl *F1 = FD1; + if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction()) + F1 = MF; + FunctionDecl *F2 = FD2; + if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction()) + F2 = MF; + llvm::SmallVector AC1, AC2; + F1->getAssociatedConstraints(AC1); + F2->getAssociatedConstraints(AC2); + bool AtLeastAsConstrained1, AtLeastAsConstrained2; + if (IsAtLeastAsConstrained(F1, AC1, F2, AC2, AtLeastAsConstrained1)) + return nullptr; + if (IsAtLeastAsConstrained(F2, AC2, F1, AC1, AtLeastAsConstrained2)) + return nullptr; + if (AtLeastAsConstrained1 == AtLeastAsConstrained2) + return nullptr; + return AtLeastAsConstrained1 ? FD1 : FD2; +} + +clang::SemaConcept::SemaConcept(Sema &S) + : SemaBase(S), SatisfactionCache(S.Context) {} + +clang::SemaConcept::~SemaConcept() { + // Delete cached satisfactions. + std::vector Satisfactions; + Satisfactions.reserve(SatisfactionCache.size()); + for (auto &Node : SatisfactionCache) + Satisfactions.push_back(&Node); + for (auto *Node : Satisfactions) + delete Node; +} + +clang::SemaConcept::SatisfactionStackResetRAII::SatisfactionStackResetRAII( + Sema &S) + : SemaRef(S) { + SemaRef.Concept().SwapSatisfactionStack(BackupSatisfactionStack); +} + +clang::SemaConcept::SatisfactionStackResetRAII::~SatisfactionStackResetRAII() { + SemaRef.Concept().SwapSatisfactionStack(BackupSatisfactionStack); +} \ No newline at end of file diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f2b9202255cd4..b4560bd41052c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -46,6 +46,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaHLSL.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" @@ -7574,7 +7575,7 @@ NamedDecl *Sema::ActOnVariableDeclarator( /*DiagID=*/0); if (const AutoType *AutoT = R->getAs()) - CheckConstrainedAuto( + SemaRef.Concept().CheckConstrainedAuto( AutoT, TInfo->getTypeLoc().getContainedAutoTypeLoc().getConceptNameLoc()); @@ -10901,7 +10902,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Precalculate whether this is a friend function template with a constraint // that depends on an enclosing template, per [temp.friend]p9. if (isFriend && FunctionTemplate && - FriendConstraintsDependOnEnclosingTemplate(NewFD)) { + Concept().FriendConstraintsDependOnEnclosingTemplate(NewFD)) { NewFD->setFriendConstraintRefersToEnclosingTemplate(true); // C++ [temp.friend]p9: @@ -18977,7 +18978,7 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record, SatisfactionStatus.push_back(true); else { ConstraintSatisfaction Satisfaction; - if (S.CheckFunctionConstraints(Method, Satisfaction)) + if (S.Concept().CheckFunctionConstraints(Method, Satisfaction)) SatisfactionStatus.push_back(false); else SatisfactionStatus.push_back(Satisfaction.IsSatisfied); @@ -19012,9 +19013,9 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record, AnotherMethodIsMoreConstrained = true; break; } - if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, OrigMethod, - {Constraints}, - AnotherMethodIsMoreConstrained)) { + if (S.Concept().IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, + OrigMethod, {Constraints}, + AnotherMethodIsMoreConstrained)) { // There was an error with the constraints comparison. Exit the loop // and don't consider this function eligible. AnotherMethodIsMoreConstrained = true; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 8225381985052..8d99f970118b4 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -43,6 +43,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenMP.h" @@ -4194,39 +4195,6 @@ void Sema::ActOnStartCXXInClassMemberInitializer() { PushFunctionScope(); } -void Sema::ActOnStartTrailingRequiresClause(Scope *S, Declarator &D) { - if (!D.isFunctionDeclarator()) - return; - auto &FTI = D.getFunctionTypeInfo(); - if (!FTI.Params) - return; - for (auto &Param : ArrayRef(FTI.Params, - FTI.NumParams)) { - auto *ParamDecl = cast(Param.Param); - if (ParamDecl->getDeclName()) - PushOnScopeChains(ParamDecl, S, /*AddToContext=*/false); - } -} - -ExprResult Sema::ActOnFinishTrailingRequiresClause(ExprResult ConstraintExpr) { - return ActOnRequiresClause(ConstraintExpr); -} - -ExprResult Sema::ActOnRequiresClause(ExprResult ConstraintExpr) { - if (ConstraintExpr.isInvalid()) - return ExprError(); - - ConstraintExpr = CorrectDelayedTyposInExpr(ConstraintExpr); - if (ConstraintExpr.isInvalid()) - return ExprError(); - - if (DiagnoseUnexpandedParameterPack(ConstraintExpr.get(), - UPPC_RequiresClause)) - return ExprError(); - - return ConstraintExpr; -} - ExprResult Sema::ConvertMemberDefaultInitExpression(FieldDecl *FD, Expr *InitExpr, SourceLocation InitLoc) { @@ -7466,7 +7434,7 @@ static bool specialMemberIsConstexpr( // Suppress duplicate constraint checking here, in case a constraint check // caused us to decide to do this. Any truely recursive checks will get // caught during these checks anyway. - Sema::SatisfactionStackResetRAII SSRAII{S}; + SemaConcept::SatisfactionStackResetRAII SSRAII{S}; // If we're inheriting a constructor, see if we need to call it for this base // class. @@ -17585,8 +17553,8 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed) << !HasMessage << Msg.str() << AssertExpr->getSourceRange(); ConstraintSatisfaction Satisfaction; - if (!CheckConstraintSatisfaction(InnerCond, Satisfaction)) - DiagnoseUnsatisfiedConstraint(Satisfaction); + if (!Concept().CheckConstraintSatisfaction(InnerCond, Satisfaction)) + Concept().DiagnoseUnsatisfiedConstraint(Satisfaction); } else if (InnerCond && !isa(InnerCond) && !isa(InnerCond)) { Diag(InnerCond->getBeginLoc(), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 5ecfdee21f09d..63d579266ba16 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -50,6 +50,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaFixItUtils.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" @@ -294,8 +295,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, // definition. if (!SkipTrailingRequiresClause && FD->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(FD, Satisfaction, Loc, - /*ForOverloadResolution*/ true)) + if (Concept().CheckFunctionConstraints(FD, Satisfaction, Loc, + /*ForOverloadResolution*/ true)) // A diagnostic will have already been generated (non-constant // constraint expression, for example) return true; @@ -303,7 +304,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, Diag(Loc, diag::err_reference_to_function_with_unsatisfied_constraints) << D; - DiagnoseUnsatisfiedConstraint(Satisfaction); + Concept().DiagnoseUnsatisfiedConstraint(Satisfaction); return true; } } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index e4601f7d6c47d..b926383dccc4a 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -39,6 +39,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaLambda.h" #include "clang/Sema/SemaObjC.h" @@ -9225,230 +9226,3 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, SourceLocation KeywordLoc, return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo); } - -concepts::Requirement *Sema::ActOnSimpleRequirement(Expr *E) { - return BuildExprRequirement(E, /*IsSimple=*/true, - /*NoexceptLoc=*/SourceLocation(), - /*ReturnTypeRequirement=*/{}); -} - -concepts::Requirement *Sema::ActOnTypeRequirement( - SourceLocation TypenameKWLoc, CXXScopeSpec &SS, SourceLocation NameLoc, - const IdentifierInfo *TypeName, TemplateIdAnnotation *TemplateId) { - assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) && - "Exactly one of TypeName and TemplateId must be specified."); - TypeSourceInfo *TSI = nullptr; - if (TypeName) { - QualType T = - CheckTypenameType(ElaboratedTypeKeyword::Typename, TypenameKWLoc, - SS.getWithLocInContext(Context), *TypeName, NameLoc, - &TSI, /*DeducedTSTContext=*/false); - if (T.isNull()) - return nullptr; - } else { - ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(), - TemplateId->NumArgs); - TypeResult T = ActOnTypenameType(CurScope, TypenameKWLoc, SS, - TemplateId->TemplateKWLoc, - TemplateId->Template, TemplateId->Name, - TemplateId->TemplateNameLoc, - TemplateId->LAngleLoc, ArgsPtr, - TemplateId->RAngleLoc); - if (T.isInvalid()) - return nullptr; - if (GetTypeFromParser(T.get(), &TSI).isNull()) - return nullptr; - } - return BuildTypeRequirement(TSI); -} - -concepts::Requirement * -Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc) { - return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, - /*ReturnTypeRequirement=*/{}); -} - -concepts::Requirement * -Sema::ActOnCompoundRequirement( - Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS, - TemplateIdAnnotation *TypeConstraint, unsigned Depth) { - // C++2a [expr.prim.req.compound] p1.3.3 - // [..] the expression is deduced against an invented function template - // F [...] F is a void function template with a single type template - // parameter T declared with the constrained-parameter. Form a new - // cv-qualifier-seq cv by taking the union of const and volatile specifiers - // around the constrained-parameter. F has a single parameter whose - // type-specifier is cv T followed by the abstract-declarator. [...] - // - // The cv part is done in the calling function - we get the concept with - // arguments and the abstract declarator with the correct CV qualification and - // have to synthesize T and the single parameter of F. - auto &II = Context.Idents.get("expr-type"); - auto *TParam = TemplateTypeParmDecl::Create(Context, CurContext, - SourceLocation(), - SourceLocation(), Depth, - /*Index=*/0, &II, - /*Typename=*/true, - /*ParameterPack=*/false, - /*HasTypeConstraint=*/true); - - if (BuildTypeConstraint(SS, TypeConstraint, TParam, - /*EllipsisLoc=*/SourceLocation(), - /*AllowUnexpandedPack=*/true)) - // Just produce a requirement with no type requirements. - return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {}); - - auto *TPL = TemplateParameterList::Create(Context, SourceLocation(), - SourceLocation(), - ArrayRef(TParam), - SourceLocation(), - /*RequiresClause=*/nullptr); - return BuildExprRequirement( - E, /*IsSimple=*/false, NoexceptLoc, - concepts::ExprRequirement::ReturnTypeRequirement(TPL)); -} - -concepts::ExprRequirement * -Sema::BuildExprRequirement( - Expr *E, bool IsSimple, SourceLocation NoexceptLoc, - concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { - auto Status = concepts::ExprRequirement::SS_Satisfied; - ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr; - if (E->isInstantiationDependent() || E->getType()->isPlaceholderType() || - ReturnTypeRequirement.isDependent()) - Status = concepts::ExprRequirement::SS_Dependent; - else if (NoexceptLoc.isValid() && canThrow(E) == CanThrowResult::CT_Can) - Status = concepts::ExprRequirement::SS_NoexceptNotMet; - else if (ReturnTypeRequirement.isSubstitutionFailure()) - Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure; - else if (ReturnTypeRequirement.isTypeConstraint()) { - // C++2a [expr.prim.req]p1.3.3 - // The immediately-declared constraint ([temp]) of decltype((E)) shall - // be satisfied. - TemplateParameterList *TPL = - ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); - QualType MatchedType = - Context.getReferenceQualifiedType(E).getCanonicalType(); - llvm::SmallVector Args; - Args.push_back(TemplateArgument(MatchedType)); - - auto *Param = cast(TPL->getParam(0)); - - MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false); - MLTAL.addOuterRetainedLevels(TPL->getDepth()); - const TypeConstraint *TC = Param->getTypeConstraint(); - assert(TC && "Type Constraint cannot be null here"); - auto *IDC = TC->getImmediatelyDeclaredConstraint(); - assert(IDC && "ImmediatelyDeclaredConstraint can't be null here."); - ExprResult Constraint = SubstExpr(IDC, MLTAL); - if (Constraint.isInvalid()) { - return new (Context) concepts::ExprRequirement( - concepts::createSubstDiagAt(*this, IDC->getExprLoc(), - [&](llvm::raw_ostream &OS) { - IDC->printPretty(OS, /*Helper=*/nullptr, - getPrintingPolicy()); - }), - IsSimple, NoexceptLoc, ReturnTypeRequirement); - } - SubstitutedConstraintExpr = - cast(Constraint.get()); - if (!SubstitutedConstraintExpr->isSatisfied()) - Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied; - } - return new (Context) concepts::ExprRequirement(E, IsSimple, NoexceptLoc, - ReturnTypeRequirement, Status, - SubstitutedConstraintExpr); -} - -concepts::ExprRequirement * -Sema::BuildExprRequirement( - concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic, - bool IsSimple, SourceLocation NoexceptLoc, - concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { - return new (Context) concepts::ExprRequirement(ExprSubstitutionDiagnostic, - IsSimple, NoexceptLoc, - ReturnTypeRequirement); -} - -concepts::TypeRequirement * -Sema::BuildTypeRequirement(TypeSourceInfo *Type) { - return new (Context) concepts::TypeRequirement(Type); -} - -concepts::TypeRequirement * -Sema::BuildTypeRequirement( - concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { - return new (Context) concepts::TypeRequirement(SubstDiag); -} - -concepts::Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) { - return BuildNestedRequirement(Constraint); -} - -concepts::NestedRequirement * -Sema::BuildNestedRequirement(Expr *Constraint) { - ConstraintSatisfaction Satisfaction; - if (!Constraint->isInstantiationDependent() && - CheckConstraintSatisfaction(nullptr, {Constraint}, /*TemplateArgs=*/{}, - Constraint->getSourceRange(), Satisfaction)) - return nullptr; - return new (Context) concepts::NestedRequirement(Context, Constraint, - Satisfaction); -} - -concepts::NestedRequirement * -Sema::BuildNestedRequirement(StringRef InvalidConstraintEntity, - const ASTConstraintSatisfaction &Satisfaction) { - return new (Context) concepts::NestedRequirement( - InvalidConstraintEntity, - ASTConstraintSatisfaction::Rebuild(Context, Satisfaction)); -} - -RequiresExprBodyDecl * -Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, - ArrayRef LocalParameters, - Scope *BodyScope) { - assert(BodyScope); - - RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(Context, CurContext, - RequiresKWLoc); - - PushDeclContext(BodyScope, Body); - - for (ParmVarDecl *Param : LocalParameters) { - if (Param->hasDefaultArg()) - // C++2a [expr.prim.req] p4 - // [...] A local parameter of a requires-expression shall not have a - // default argument. [...] - Diag(Param->getDefaultArgRange().getBegin(), - diag::err_requires_expr_local_parameter_default_argument); - // Ignore default argument and move on - - Param->setDeclContext(Body); - // If this has an identifier, add it to the scope stack. - if (Param->getIdentifier()) { - CheckShadow(BodyScope, Param); - PushOnScopeChains(Param, BodyScope); - } - } - return Body; -} - -void Sema::ActOnFinishRequiresExpr() { - assert(CurContext && "DeclContext imbalance!"); - CurContext = CurContext->getLexicalParent(); - assert(CurContext && "Popped translation unit!"); -} - -ExprResult Sema::ActOnRequiresExpr( - SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, - SourceLocation LParenLoc, ArrayRef LocalParameters, - SourceLocation RParenLoc, ArrayRef Requirements, - SourceLocation ClosingBraceLoc) { - auto *RE = RequiresExpr::Create(Context, RequiresKWLoc, Body, LParenLoc, - LocalParameters, RParenLoc, Requirements, - ClosingBraceLoc); - if (DiagnoseUnexpandedParameterPackInRequiresExpr(RE)) - return ExprError(); - return RE; -} diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 2eb25237a0de6..7947c061d6a9c 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -32,6 +32,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Overload.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/Template.h" @@ -1517,8 +1518,8 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New, *OldRC = Old->getTrailingRequiresClause(); if ((NewRC != nullptr) != (OldRC != nullptr)) return true; - if (NewRC && - !SemaRef.AreConstraintExpressionsEqual(OldDecl, OldRC, NewDecl, NewRC)) + if (NewRC && !SemaRef.Concept().AreConstraintExpressionsEqual( + OldDecl, OldRC, NewDecl, NewRC)) return true; } @@ -7093,8 +7094,8 @@ void Sema::AddOverloadCandidate( if (Function->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(Function, Satisfaction, /*Loc*/ {}, - /*ForOverloadResolution*/ true) || + if (Concept().CheckFunctionConstraints(Function, Satisfaction, /*Loc*/ {}, + /*ForOverloadResolution*/ true) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -7611,8 +7612,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, if (Method->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(Method, Satisfaction, /*Loc*/ {}, - /*ForOverloadResolution*/ true) || + if (Concept().CheckFunctionConstraints(Method, Satisfaction, /*Loc*/ {}, + /*ForOverloadResolution*/ true) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -8041,7 +8042,7 @@ void Sema::AddConversionCandidate( if (Conversion->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(Conversion, Satisfaction) || + if (Concept().CheckFunctionConstraints(Conversion, Satisfaction) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -8324,8 +8325,8 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, if (Conversion->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(Conversion, Satisfaction, /*Loc*/ {}, - /*ForOverloadResolution*/ true) || + if (Concept().CheckFunctionConstraints(Conversion, Satisfaction, /*Loc*/ {}, + /*ForOverloadResolution*/ true) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -10635,7 +10636,7 @@ bool clang::isBetterOverloadCandidate( // parameter-type-lists, and F1 is more constrained than F2 [...], if (!Cand1IsSpecialization && !Cand2IsSpecialization && sameFunctionParameterTypeLists(S, Cand1, Cand2) && - S.getMoreConstrainedFunction(Cand1.Function, Cand2.Function) == + S.Concept().getMoreConstrainedFunction(Cand1.Function, Cand2.Function) == Cand1.Function) return true; @@ -11070,7 +11071,7 @@ static bool checkAddressOfFunctionIsAvailable(Sema &S, const FunctionDecl *FD, if (FD->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (S.CheckFunctionConstraints(FD, Satisfaction, Loc)) + if (S.Concept().CheckFunctionConstraints(FD, Satisfaction, Loc)) return false; if (!Satisfaction.IsSatisfied) { if (Complain) { @@ -11089,7 +11090,7 @@ static bool checkAddressOfFunctionIsAvailable(Sema &S, const FunctionDecl *FD, } else S.Diag(Loc, diag::err_addrof_function_constraints_not_satisfied) << FD; - S.DiagnoseUnsatisfiedConstraint(Satisfaction); + S.Concept().DiagnoseUnsatisfiedConstraint(Satisfaction); } return false; } @@ -11220,8 +11221,8 @@ MaybeDiagnoseAmbiguousConstraints(Sema &S, ArrayRef Cands) { return; // The diagnostic can only happen if there are associated constraints on // both sides (there needs to be some identical atomic constraint). - if (S.MaybeEmitAmbiguousAtomicConstraintsDiagnostic(FirstCand, FirstAC, - SecondCand, SecondAC)) + if (S.Concept().MaybeEmitAmbiguousAtomicConstraintsDiagnostic( + FirstCand, FirstAC, SecondCand, SecondAC)) // Just show the user one diagnostic, they'll probably figure it out // from here. return; @@ -11746,8 +11747,8 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated, diag::note_ovl_candidate_unsatisfied_constraints) << TemplateArgString; - S.DiagnoseUnsatisfiedConstraint( - static_cast(DeductionFailure.Data)->Satisfaction); + S.Concept().DiagnoseUnsatisfiedConstraint( + static_cast(DeductionFailure.Data)->Satisfaction); return; } case TemplateDeductionResult::TooManyArguments: @@ -12134,9 +12135,9 @@ 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.Concept().CheckFunctionConstraints(Fn, Satisfaction)) break; - S.DiagnoseUnsatisfiedConstraint(Satisfaction); + S.Concept().DiagnoseUnsatisfiedConstraint(Satisfaction); } } } @@ -12178,8 +12179,8 @@ static void NoteSurrogateCandidate(Sema &S, OverloadCandidate *Cand) { diag::note_ovl_surrogate_constraints_not_satisfied) << Cand->Surrogate; ConstraintSatisfaction Satisfaction; - if (S.CheckFunctionConstraints(Cand->Surrogate, Satisfaction)) - S.DiagnoseUnsatisfiedConstraint(Satisfaction); + if (S.Concept().CheckFunctionConstraints(Cand->Surrogate, Satisfaction)) + S.Concept().DiagnoseUnsatisfiedConstraint(Satisfaction); } else { S.Diag(Cand->Surrogate->getLocation(), diag::note_ovl_surrogate_cand) << FnType; @@ -13341,7 +13342,8 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) { } // FD has the same CUDA prefernece than Result. Continue check // constraints. - FunctionDecl *MoreConstrained = getMoreConstrainedFunction(FD, Result); + FunctionDecl *MoreConstrained = + Concept().getMoreConstrainedFunction(FD, Result); if (MoreConstrained != FD) { if (!MoreConstrained) { IsResultAmbiguous = true; @@ -13367,7 +13369,7 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) { // constraints. if (getLangOpts().CUDA && CheckCUDAPreference(Skipped, Result) != 0) continue; - if (!getMoreConstrainedFunction(Skipped, Result)) + if (!Concept().getMoreConstrainedFunction(Skipped, Result)) return nullptr; } Pair = DAP; @@ -13822,7 +13824,7 @@ DiagnoseTwoPhaseOperatorLookup(Sema &SemaRef, OverloadedOperatorKind Op, namespace { class BuildRecoveryCallExprRAII { Sema &SemaRef; - Sema::SatisfactionStackResetRAII SatStack; + SemaConcept::SatisfactionStackResetRAII SatStack; public: BuildRecoveryCallExprRAII(Sema &S) : SemaRef(S), SatStack(S) { diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 4937cce4621f0..3083a0909a35f 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -34,6 +34,7 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" @@ -1098,217 +1099,16 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename, } /// Convert the parser's template argument list representation into our form. -static TemplateArgumentListInfo -makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) { +TemplateArgumentListInfo +Sema::makeTemplateArgumentListInfo(TemplateIdAnnotation &TemplateId) { TemplateArgumentListInfo TemplateArgs(TemplateId.LAngleLoc, TemplateId.RAngleLoc); ASTTemplateArgsPtr TemplateArgsPtr(TemplateId.getTemplateArgs(), TemplateId.NumArgs); - S.translateTemplateArguments(TemplateArgsPtr, TemplateArgs); + translateTemplateArguments(TemplateArgsPtr, TemplateArgs); return TemplateArgs; } -bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) { - - TemplateName TN = TypeConstr->Template.get(); - ConceptDecl *CD = cast(TN.getAsTemplateDecl()); - - // C++2a [temp.param]p4: - // [...] The concept designated by a type-constraint shall be a type - // concept ([temp.concept]). - if (!CD->isTypeConcept()) { - Diag(TypeConstr->TemplateNameLoc, - diag::err_type_constraint_non_type_concept); - return true; - } - - bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid(); - - if (!WereArgsSpecified && - CD->getTemplateParameters()->getMinRequiredArguments() > 1) { - Diag(TypeConstr->TemplateNameLoc, - diag::err_type_constraint_missing_arguments) - << CD; - return true; - } - return false; -} - -bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS, - TemplateIdAnnotation *TypeConstr, - TemplateTypeParmDecl *ConstrainedParameter, - SourceLocation EllipsisLoc) { - return BuildTypeConstraint(SS, TypeConstr, ConstrainedParameter, EllipsisLoc, - false); -} - -bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS, - TemplateIdAnnotation *TypeConstr, - TemplateTypeParmDecl *ConstrainedParameter, - SourceLocation EllipsisLoc, - bool AllowUnexpandedPack) { - - if (CheckTypeConstraint(TypeConstr)) - return true; - - TemplateName TN = TypeConstr->Template.get(); - ConceptDecl *CD = cast(TN.getAsTemplateDecl()); - UsingShadowDecl *USD = TN.getAsUsingShadowDecl(); - - DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name), - TypeConstr->TemplateNameLoc); - - TemplateArgumentListInfo TemplateArgs; - if (TypeConstr->LAngleLoc.isValid()) { - TemplateArgs = - makeTemplateArgumentListInfo(*this, *TypeConstr); - - if (EllipsisLoc.isInvalid() && !AllowUnexpandedPack) { - for (TemplateArgumentLoc Arg : TemplateArgs.arguments()) { - if (DiagnoseUnexpandedParameterPack(Arg, UPPC_TypeConstraint)) - return true; - } - } - } - return AttachTypeConstraint( - SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(), - ConceptName, CD, /*FoundDecl=*/USD ? cast(USD) : CD, - TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, - ConstrainedParameter, EllipsisLoc); -} - -template -static ExprResult formImmediatelyDeclaredConstraint( - Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, - ConceptDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc, - SourceLocation RAngleLoc, QualType ConstrainedType, - SourceLocation ParamNameLoc, ArgumentLocAppender Appender, - SourceLocation EllipsisLoc) { - - TemplateArgumentListInfo ConstraintArgs; - ConstraintArgs.addArgument( - S.getTrivialTemplateArgumentLoc(TemplateArgument(ConstrainedType), - /*NTTPType=*/QualType(), ParamNameLoc)); - - ConstraintArgs.setRAngleLoc(RAngleLoc); - ConstraintArgs.setLAngleLoc(LAngleLoc); - Appender(ConstraintArgs); - - // C++2a [temp.param]p4: - // [...] This constraint-expression E is called the immediately-declared - // constraint of T. [...] - CXXScopeSpec SS; - SS.Adopt(NS); - ExprResult ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId( - SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, - /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, NamedConcept, - &ConstraintArgs); - if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid()) - return ImmediatelyDeclaredConstraint; - - // C++2a [temp.param]p4: - // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). - // - // We have the following case: - // - // template concept C1 = true; - // template struct s1; - // - // The constraint: (C1 && ...) - // - // Note that the type of C1 is known to be 'bool', so we don't need to do - // any unqualified lookups for 'operator&&' here. - return S.BuildCXXFoldExpr(/*UnqualifiedLookup=*/nullptr, - /*LParenLoc=*/SourceLocation(), - ImmediatelyDeclaredConstraint.get(), BO_LAnd, - EllipsisLoc, /*RHS=*/nullptr, - /*RParenLoc=*/SourceLocation(), - /*NumExpansions=*/std::nullopt); -} - -/// Attach a type-constraint to a template parameter. -/// \returns true if an error occurred. This can happen if the -/// immediately-declared constraint could not be formed (e.g. incorrect number -/// of arguments for the named concept). -bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, - DeclarationNameInfo NameInfo, - ConceptDecl *NamedConcept, NamedDecl *FoundDecl, - const TemplateArgumentListInfo *TemplateArgs, - TemplateTypeParmDecl *ConstrainedParameter, - SourceLocation EllipsisLoc) { - // C++2a [temp.param]p4: - // [...] If Q is of the form C, then let E' be - // C. Otherwise, let E' be C. [...] - const ASTTemplateArgumentListInfo *ArgsAsWritten = - TemplateArgs ? ASTTemplateArgumentListInfo::Create(Context, - *TemplateArgs) : nullptr; - - QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0); - - ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint( - *this, NS, NameInfo, NamedConcept, FoundDecl, - TemplateArgs ? TemplateArgs->getLAngleLoc() : SourceLocation(), - TemplateArgs ? TemplateArgs->getRAngleLoc() : SourceLocation(), - ParamAsArgument, ConstrainedParameter->getLocation(), - [&](TemplateArgumentListInfo &ConstraintArgs) { - if (TemplateArgs) - for (const auto &ArgLoc : TemplateArgs->arguments()) - ConstraintArgs.addArgument(ArgLoc); - }, - EllipsisLoc); - if (ImmediatelyDeclaredConstraint.isInvalid()) - return true; - - auto *CL = ConceptReference::Create(Context, /*NNS=*/NS, - /*TemplateKWLoc=*/SourceLocation{}, - /*ConceptNameInfo=*/NameInfo, - /*FoundDecl=*/FoundDecl, - /*NamedConcept=*/NamedConcept, - /*ArgsWritten=*/ArgsAsWritten); - ConstrainedParameter->setTypeConstraint(CL, - ImmediatelyDeclaredConstraint.get()); - return false; -} - -bool Sema::AttachTypeConstraint(AutoTypeLoc TL, - NonTypeTemplateParmDecl *NewConstrainedParm, - NonTypeTemplateParmDecl *OrigConstrainedParm, - SourceLocation EllipsisLoc) { - if (NewConstrainedParm->getType() != TL.getType() || - TL.getAutoKeyword() != AutoTypeKeyword::Auto) { - Diag(NewConstrainedParm->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), - diag::err_unsupported_placeholder_constraint) - << NewConstrainedParm->getTypeSourceInfo() - ->getTypeLoc() - .getSourceRange(); - return true; - } - // FIXME: Concepts: This should be the type of the placeholder, but this is - // unclear in the wording right now. - DeclRefExpr *Ref = - BuildDeclRefExpr(OrigConstrainedParm, OrigConstrainedParm->getType(), - VK_PRValue, OrigConstrainedParm->getLocation()); - if (!Ref) - return true; - ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint( - *this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(), - TL.getNamedConcept(), /*FoundDecl=*/TL.getFoundDecl(), TL.getLAngleLoc(), - TL.getRAngleLoc(), BuildDecltypeType(Ref), - OrigConstrainedParm->getLocation(), - [&](TemplateArgumentListInfo &ConstraintArgs) { - for (unsigned I = 0, C = TL.getNumArgs(); I != C; ++I) - ConstraintArgs.addArgument(TL.getArgLoc(I)); - }, - EllipsisLoc); - if (ImmediatelyDeclaredConstraint.isInvalid() || - !ImmediatelyDeclaredConstraint.isUsable()) - return true; - - NewConstrainedParm->setPlaceholderTypeConstraint( - ImmediatelyDeclaredConstraint.get()); - return false; -} - /// Check that the type of a non-type template parameter is /// well-formed. /// @@ -1585,7 +1385,7 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc()) if (TL.isConstrained()) - if (AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc())) + if (Concept().AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc())) Invalid = true; if (Invalid) @@ -5166,8 +4966,8 @@ static void checkMoreSpecializedThanPrimary(Sema &S, PartialSpecDecl *Partial) { SmallVector PartialAC, TemplateAC; Template->getAssociatedConstraints(TemplateAC); Partial->getAssociatedConstraints(PartialAC); - S.MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Partial, PartialAC, Template, - TemplateAC); + S.Concept().MaybeEmitAmbiguousAtomicConstraintsDiagnostic( + Partial, PartialAC, Template, TemplateAC); } static void @@ -5261,7 +5061,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization( TemplateIdAnnotation *TemplateId = D.getName().TemplateId; TemplateArgumentListInfo TemplateArgs = - makeTemplateArgumentListInfo(*this, *TemplateId); + makeTemplateArgumentListInfo(*TemplateId); SourceLocation TemplateNameLoc = D.getIdentifierLoc(); SourceLocation LAngleLoc = TemplateId->LAngleLoc; SourceLocation RAngleLoc = TemplateId->RAngleLoc; @@ -5636,55 +5436,6 @@ void Sema::diagnoseMissingTemplateArguments(TemplateName Name, } } -ExprResult -Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - const DeclarationNameInfo &ConceptNameInfo, - NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, - const TemplateArgumentListInfo *TemplateArgs) { - assert(NamedConcept && "A concept template id without a template?"); - - llvm::SmallVector SugaredConverted, CanonicalConverted; - if (CheckTemplateArgumentList( - NamedConcept, ConceptNameInfo.getLoc(), - const_cast(*TemplateArgs), - /*PartialTemplateArgs=*/false, SugaredConverted, CanonicalConverted, - /*UpdateArgsWithConversions=*/false)) - return ExprError(); - - DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc()); - - auto *CSD = ImplicitConceptSpecializationDecl::Create( - Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(), - CanonicalConverted); - ConstraintSatisfaction Satisfaction; - bool AreArgsDependent = - TemplateSpecializationType::anyDependentTemplateArguments( - *TemplateArgs, CanonicalConverted); - MultiLevelTemplateArgumentList MLTAL(NamedConcept, CanonicalConverted, - /*Final=*/false); - LocalInstantiationScope Scope(*this); - - EnterExpressionEvaluationContext EECtx{ - *this, ExpressionEvaluationContext::ConstantEvaluated, CSD}; - - if (!AreArgsDependent && - CheckConstraintSatisfaction( - NamedConcept, {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)); - return ConceptSpecializationExpr::Create( - Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction); -} - ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R, @@ -5723,9 +5474,9 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, } if (R.getAsSingle()) { - return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo(), - R.getRepresentativeDecl(), - R.getAsSingle(), TemplateArgs); + return Concept().CheckConceptTemplateId( + SS, TemplateKWLoc, R.getLookupNameInfo(), R.getRepresentativeDecl(), + R.getAsSingle(), TemplateArgs); } // We don't want lookup warnings at this point. @@ -6946,7 +6697,7 @@ bool Sema::CheckTemplateArgumentList( /*RelativeToPrimary=*/true, /*Pattern=*/nullptr, /*ForConceptInstantiation=*/true); - if (EnsureTemplateArgumentListConstraints( + if (Concept().EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { if (ConstraintsNotSatisfied) @@ -8545,8 +8296,9 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, Template->getAssociatedConstraints(TemplateAC); bool IsParamAtLeastAsConstrained; - if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, - IsParamAtLeastAsConstrained)) + if (Concept().IsAtLeastAsConstrained(Param, ParamsAC, Template, + TemplateAC, + IsParamAtLeastAsConstrained)) return true; if (!IsParamAtLeastAsConstrained) { Diag(Arg.getLocation(), @@ -8555,8 +8307,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, Diag(Param->getLocation(), diag::note_entity_declared_at) << Param; Diag(Template->getLocation(), diag::note_entity_declared_at) << Template; - MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template, - TemplateAC); + Concept().MaybeEmitAmbiguousAtomicConstraintsDiagnostic( + Param, ParamsAC, Template, TemplateAC); return true; } return false; @@ -8883,9 +8635,8 @@ Sema::BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, /// Match two template parameters within template parameter lists. static bool MatchTemplateParameterKind( - Sema &S, NamedDecl *New, - const Sema::TemplateCompareNewDeclInfo &NewInstFrom, NamedDecl *Old, - const NamedDecl *OldInstFrom, bool Complain, + Sema &S, NamedDecl *New, const TemplateCompareNewDeclInfo &NewInstFrom, + NamedDecl *Old, const NamedDecl *OldInstFrom, bool Complain, Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { // Check the actual kind (type, non-type, template). if (Old->getKind() != New->getKind()) { @@ -9019,8 +8770,8 @@ static bool MatchTemplateParameterKind( } if (NewC) { - if (!S.AreConstraintExpressionsEqual(OldInstFrom, OldC, NewInstFrom, - NewC)) { + if (!S.Concept().AreConstraintExpressionsEqual(OldInstFrom, OldC, + NewInstFrom, NewC)) { if (Complain) Diagnose(); return false; @@ -9161,8 +8912,8 @@ bool Sema::TemplateParameterListsAreEqual( } if (NewRC) { - if (!AreConstraintExpressionsEqual(OldInstFrom, OldRC, NewInstFrom, - NewRC)) { + if (!Concept().AreConstraintExpressionsEqual(OldInstFrom, OldRC, + NewInstFrom, NewRC)) { if (Complain) Diagnose(); return false; @@ -9601,7 +9352,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization( // Translate the parser's template argument list in our AST format. TemplateArgumentListInfo TemplateArgs = - makeTemplateArgumentListInfo(*this, TemplateId); + makeTemplateArgumentListInfo(TemplateId); // Check for unexpanded parameter packs in any of the template arguments. for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I) @@ -9859,119 +9610,6 @@ Decl *Sema::ActOnTemplateDeclarator(Scope *S, return NewDecl; } -Decl *Sema::ActOnConceptDefinition( - Scope *S, MultiTemplateParamsArg TemplateParameterLists, - const IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr, - const ParsedAttributesView &Attrs) { - DeclContext *DC = CurContext; - - if (!DC->getRedeclContext()->isFileContext()) { - Diag(NameLoc, - diag::err_concept_decls_may_only_appear_in_global_namespace_scope); - return nullptr; - } - - if (TemplateParameterLists.size() > 1) { - Diag(NameLoc, diag::err_concept_extra_headers); - return nullptr; - } - - TemplateParameterList *Params = TemplateParameterLists.front(); - - if (Params->size() == 0) { - Diag(NameLoc, diag::err_concept_no_parameters); - return nullptr; - } - - // Ensure that the parameter pack, if present, is the last parameter in the - // template. - for (TemplateParameterList::const_iterator ParamIt = Params->begin(), - ParamEnd = Params->end(); - ParamIt != ParamEnd; ++ParamIt) { - Decl const *Param = *ParamIt; - if (Param->isParameterPack()) { - if (++ParamIt == ParamEnd) - break; - Diag(Param->getLocation(), - diag::err_template_param_pack_must_be_last_template_parameter); - return nullptr; - } - } - - if (DiagnoseUnexpandedParameterPack(ConstraintExpr)) - return nullptr; - - ConceptDecl *NewDecl = - ConceptDecl::Create(Context, DC, NameLoc, Name, Params, ConstraintExpr); - - if (NewDecl->hasAssociatedConstraints()) { - // C++2a [temp.concept]p4: - // A concept shall not have associated constraints. - Diag(NameLoc, diag::err_concept_no_associated_constraints); - NewDecl->setInvalidDecl(); - } - - // Check for conflicting previous declaration. - DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NameLoc); - LookupResult Previous(*this, NameInfo, LookupOrdinaryName, - forRedeclarationInCurContext()); - LookupName(Previous, S); - FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage=*/false, - /*AllowInlineNamespace*/false); - bool AddToScope = true; - CheckConceptRedefinition(NewDecl, Previous, AddToScope); - - ActOnDocumentableDecl(NewDecl); - if (AddToScope) - PushOnScopeChains(NewDecl, S); - - ProcessDeclAttributeList(S, NewDecl, Attrs); - - return NewDecl; -} - -void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl, - LookupResult &Previous, bool &AddToScope) { - AddToScope = true; - - if (Previous.empty()) - return; - - auto *OldConcept = dyn_cast(Previous.getRepresentativeDecl()->getUnderlyingDecl()); - if (!OldConcept) { - auto *Old = Previous.getRepresentativeDecl(); - Diag(NewDecl->getLocation(), diag::err_redefinition_different_kind) - << NewDecl->getDeclName(); - notePreviousDefinition(Old, NewDecl->getLocation()); - AddToScope = false; - return; - } - // Check if we can merge with a concept declaration. - bool IsSame = Context.isSameEntity(NewDecl, OldConcept); - if (!IsSame) { - Diag(NewDecl->getLocation(), diag::err_redefinition_different_concept) - << NewDecl->getDeclName(); - notePreviousDefinition(OldConcept, NewDecl->getLocation()); - AddToScope = false; - return; - } - if (hasReachableDefinition(OldConcept) && - IsRedefinitionInModule(NewDecl, OldConcept)) { - Diag(NewDecl->getLocation(), diag::err_redefinition) - << NewDecl->getDeclName(); - notePreviousDefinition(OldConcept, NewDecl->getLocation()); - AddToScope = false; - return; - } - if (!Previous.isSingleResult()) { - // FIXME: we should produce an error in case of ambig and failed lookups. - // Other decls (e.g. namespaces) also have this shortcoming. - return; - } - // We unwrap canonical decl late to check for module visibility. - Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl()); -} - /// \brief Strips various properties off an implicit instantiation /// that has just been explicitly specialized. static void StripImplicitInstantiation(NamedDecl *D, bool MinGW) { @@ -10566,14 +10204,15 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) { continue; if (ConstraintSatisfaction Satisfaction; Method->getTrailingRequiresClause() && - (CheckFunctionConstraints(Method, Satisfaction, - /*UsageLoc=*/Member->getLocation(), - /*ForOverloadResolution=*/true) || + (Concept().CheckFunctionConstraints( + Method, Satisfaction, + /*UsageLoc=*/Member->getLocation(), + /*ForOverloadResolution=*/true) || !Satisfaction.IsSatisfied)) continue; Candidates.push_back(Method); FunctionDecl *MoreConstrained = - Instantiation ? getMoreConstrainedFunction( + Instantiation ? Concept().getMoreConstrainedFunction( Method, cast(Instantiation)) : Method; if (!MoreConstrained) { @@ -11437,7 +11076,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, // Translate the parser's template argument list into our AST format. TemplateArgumentListInfo TemplateArgs = - makeTemplateArgumentListInfo(*this, *D.getName().TemplateId); + makeTemplateArgumentListInfo(*D.getName().TemplateId); DeclResult Res = CheckVarTemplateId(PrevTemplate, TemplateLoc, D.getIdentifierLoc(), TemplateArgs); @@ -11518,7 +11157,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, bool HasExplicitTemplateArgs = false; TemplateArgumentListInfo TemplateArgs; if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) { - TemplateArgs = makeTemplateArgumentListInfo(*this, *D.getName().TemplateId); + TemplateArgs = makeTemplateArgumentListInfo(*D.getName().TemplateId); HasExplicitTemplateArgs = true; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 41fd210f29d09..dacfa18cfb467 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -39,6 +39,7 @@ #include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/Ownership.h" #include "clang/Sema/Sema.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "llvm/ADT/APInt.h" @@ -3088,9 +3089,9 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template, if (!Innermost) MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs); - if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, - Info.getLocation(), - Info.AssociatedConstraintsSatisfaction) || + if (S.Concept().CheckConstraintSatisfaction( + Template, AssociatedConstraints, MLTAL, Info.getLocation(), + Info.AssociatedConstraintsSatisfaction) || !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { Info.reset( TemplateArgumentList::CreateCopy(S.Context, SugaredDeducedArgs), @@ -3940,7 +3941,7 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( if (!PartialOverloading || (CanonicalBuilder.size() == FunctionTemplate->getTemplateParameters()->size())) { - if (CheckInstantiatedFunctionTemplateConstraints( + if (Concept().CheckInstantiatedFunctionTemplateConstraints( Info.getLocation(), Specialization, CanonicalBuilder, Info.AssociatedConstraintsSatisfaction)) return TemplateDeductionResult::MiscellaneousDeductionFailure; @@ -5100,9 +5101,9 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, return true; MultiLevelTemplateArgumentList MLTAL(Concept, CanonicalConverted, /*Final=*/false); - if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, - MLTAL, TypeLoc.getLocalSourceRange(), - Satisfaction)) + if (S.Concept().CheckConstraintSatisfaction( + Concept, {Concept->getConstraintExpr()}, MLTAL, + TypeLoc.getLocalSourceRange(), Satisfaction)) return true; if (!Satisfaction.IsSatisfied) { std::string Buf; @@ -5118,7 +5119,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, S.Diag(TypeLoc.getConceptNameLoc(), diag::err_placeholder_constraints_not_satisfied) << Deduced << Buf << TypeLoc.getLocalSourceRange(); - S.DiagnoseUnsatisfiedConstraint(Satisfaction); + S.Concept().DiagnoseUnsatisfiedConstraint(Satisfaction); return true; } return false; @@ -5853,9 +5854,11 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate( FT1->getAssociatedConstraints(AC1); FT2->getAssociatedConstraints(AC2); bool AtLeastAsConstrained1, AtLeastAsConstrained2; - if (IsAtLeastAsConstrained(FT1, AC1, FT2, AC2, AtLeastAsConstrained1)) + if (Concept().IsAtLeastAsConstrained(FT1, AC1, FT2, AC2, + AtLeastAsConstrained1)) return nullptr; - if (IsAtLeastAsConstrained(FT2, AC2, FT1, AC1, AtLeastAsConstrained2)) + if (Concept().IsAtLeastAsConstrained(FT2, AC2, FT1, AC1, + AtLeastAsConstrained2)) return nullptr; if (AtLeastAsConstrained1 == AtLeastAsConstrained2) return nullptr; @@ -5973,38 +5976,6 @@ UnresolvedSetIterator Sema::getMostSpecialized( return SpecEnd; } -/// Returns the more constrained function according to the rules of -/// partial ordering by constraints (C++ [temp.constr.order]). -/// -/// \param FD1 the first function -/// -/// \param FD2 the second function -/// -/// \returns the more constrained function. If neither function is -/// more constrained, returns NULL. -FunctionDecl *Sema::getMoreConstrainedFunction(FunctionDecl *FD1, - FunctionDecl *FD2) { - assert(!FD1->getDescribedTemplate() && !FD2->getDescribedTemplate() && - "not for function templates"); - FunctionDecl *F1 = FD1; - if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction()) - F1 = MF; - FunctionDecl *F2 = FD2; - if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction()) - F2 = MF; - llvm::SmallVector AC1, AC2; - F1->getAssociatedConstraints(AC1); - F2->getAssociatedConstraints(AC2); - bool AtLeastAsConstrained1, AtLeastAsConstrained2; - if (IsAtLeastAsConstrained(F1, AC1, F2, AC2, AtLeastAsConstrained1)) - return nullptr; - if (IsAtLeastAsConstrained(F2, AC2, F1, AC1, AtLeastAsConstrained2)) - return nullptr; - if (AtLeastAsConstrained1 == AtLeastAsConstrained2) - return nullptr; - return AtLeastAsConstrained1 ? FD1 : FD2; -} - /// Determine whether one partial specialization, P1, is at least as /// specialized than another, P2. /// @@ -6231,10 +6202,12 @@ getMoreSpecialized(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P1, P1->getAssociatedConstraints(AC1); P2->getAssociatedConstraints(AC2); bool AtLeastAsConstrained1, AtLeastAsConstrained2; - if (S.IsAtLeastAsConstrained(P1, AC1, P2, AC2, AtLeastAsConstrained1) || + if (S.Concept().IsAtLeastAsConstrained(P1, AC1, P2, AC2, + AtLeastAsConstrained1) || (IsMoreSpecialThanPrimaryCheck && !AtLeastAsConstrained1)) return nullptr; - if (S.IsAtLeastAsConstrained(P2, AC2, P1, AC1, AtLeastAsConstrained2)) + if (S.Concept().IsAtLeastAsConstrained(P2, AC2, P1, AC1, + AtLeastAsConstrained2)) return nullptr; if (AtLeastAsConstrained1 == AtLeastAsConstrained2) return nullptr; diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 07626058c7977..2bb7b1f99af50 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2802,7 +2802,7 @@ TemplateInstantiator::TransformNestedRequirement( if (ConstrInst.isInvalid()) return nullptr; llvm::SmallVector Result; - if (!SemaRef.CheckConstraintSatisfaction( + if (!SemaRef.Concept().CheckConstraintSatisfaction( nullptr, {Req->getConstraintExpr()}, Result, TemplateArgs, Req->getConstraintExpr()->getSourceRange(), Satisfaction) && !Result.empty()) @@ -3131,7 +3131,7 @@ bool Sema::SubstTypeConstraint( InstArgs)) return true; } - return AttachTypeConstraint( + return Concept().AttachTypeConstraint( TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(), TC->getNamedConcept(), /*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs, Inst, @@ -4135,7 +4135,7 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation, if (Function->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(Function, Satisfaction) || + if (Concept().CheckFunctionConstraints(Function, Satisfaction) || !Satisfaction.IsSatisfied) { continue; } @@ -4634,3 +4634,41 @@ NamedDecl *LocalInstantiationScope::getPartiallySubstitutedPack( return nullptr; } + +bool Sema::addInstantiatedCapturesToScope( + FunctionDecl *Function, const FunctionDecl *PatternDecl, + LocalInstantiationScope &Scope, + const MultiLevelTemplateArgumentList &TemplateArgs) { + const auto *LambdaClass = cast(Function)->getParent(); + const auto *LambdaPattern = cast(PatternDecl)->getParent(); + + unsigned Instantiated = 0; + + auto AddSingleCapture = [&](const ValueDecl *CapturedPattern, + unsigned Index) { + ValueDecl *CapturedVar = LambdaClass->getCapture(Index)->getCapturedVar(); + if (CapturedVar->isInitCapture()) + Scope.InstantiatedLocal(CapturedPattern, CapturedVar); + }; + + for (const LambdaCapture &CapturePattern : LambdaPattern->captures()) { + if (!CapturePattern.capturesVariable()) { + Instantiated++; + continue; + } + const ValueDecl *CapturedPattern = CapturePattern.getCapturedVar(); + if (!CapturedPattern->isParameterPack()) { + AddSingleCapture(CapturedPattern, Instantiated++); + } else { + Scope.MakeInstantiatedLocalArgPack(CapturedPattern); + std::optional NumArgumentsInExpansion = + SemaRef.getNumArgumentsInExpansion(CapturedPattern->getType(), + TemplateArgs); + if (!NumArgumentsInExpansion) + continue; + for (unsigned Arg = 0; Arg < *NumArgumentsInExpansion; ++Arg) + AddSingleCapture(CapturedPattern, Instantiated++); + } + } + return false; +} diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 381d79b2fcd46..e13f5ecfe724e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -27,6 +27,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenMP.h" @@ -3111,8 +3112,9 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( // Note: We attach the uninstantiated constriant here, so that it can be // instantiated relative to the top level, like all our other // constraints. - if (SemaRef.AttachTypeConstraint(AutoLoc, /*NewConstrainedParm=*/Param, - /*OrigConstrainedParm=*/D, EllipsisLoc)) + if (SemaRef.Concept().AttachTypeConstraint( + AutoLoc, /*NewConstrainedParm=*/Param, + /*OrigConstrainedParm=*/D, EllipsisLoc)) Invalid = true; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index ef0b6b701a52c..f9f0ee6d99c9a 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -34,6 +34,7 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" #include "clang/Sema/SemaOpenMP.h" @@ -3089,7 +3090,7 @@ InventTemplateParameter(TypeProcessingState &state, QualType T, } if (!Invalid) { - S.AttachTypeConstraint( + S.Concept().AttachTypeConstraint( AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(), AutoLoc.getNamedConcept(), /*FoundDecl=*/AutoLoc.getFoundDecl(), AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, @@ -3122,7 +3123,7 @@ InventTemplateParameter(TypeProcessingState &state, QualType T, TemplateId->Template.get().getAsUsingShadowDecl(); auto *CD = cast(TemplateId->Template.get().getAsTemplateDecl()); - S.AttachTypeConstraint( + S.Concept().AttachTypeConstraint( D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context), DeclarationNameInfo(DeclarationName(TemplateId->Name), TemplateId->TemplateNameLoc), @@ -6393,7 +6394,7 @@ TypeResult Sema::ActOnTypeName(Declarator &D) { } if (const AutoType *AutoT = T->getAs()) - CheckConstrainedAuto( + SemaRef.Concept().CheckConstrainedAuto( AutoT, TInfo->getTypeLoc().getContainedAutoTypeLoc().getConceptNameLoc()); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 29444f0edc2ae..f063030937837 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -37,6 +37,7 @@ #include "clang/Sema/Ownership.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/SemaConcept.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/SemaObjC.h" @@ -3662,10 +3663,8 @@ class TreeTransform { TemplateArgumentListInfo *TALI) { CXXScopeSpec SS; SS.Adopt(NNS); - ExprResult Result = getSema().CheckConceptTemplateId(SS, TemplateKWLoc, - ConceptNameInfo, - FoundDecl, - NamedConcept, TALI); + ExprResult Result = getSema().Concept().CheckConceptTemplateId( + SS, TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept, TALI); if (Result.isInvalid()) return ExprError(); return Result; @@ -3690,11 +3689,11 @@ class TreeTransform { concepts::TypeRequirement * RebuildTypeRequirement( concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { - return SemaRef.BuildTypeRequirement(SubstDiag); + return SemaRef.Concept().BuildTypeRequirement(SubstDiag); } concepts::TypeRequirement *RebuildTypeRequirement(TypeSourceInfo *T) { - return SemaRef.BuildTypeRequirement(T); + return SemaRef.Concept().BuildTypeRequirement(T); } concepts::ExprRequirement * @@ -3702,26 +3701,26 @@ class TreeTransform { concepts::Requirement::SubstitutionDiagnostic *SubstDiag, bool IsSimple, SourceLocation NoexceptLoc, concepts::ExprRequirement::ReturnTypeRequirement Ret) { - return SemaRef.BuildExprRequirement(SubstDiag, IsSimple, NoexceptLoc, - std::move(Ret)); + return SemaRef.Concept().BuildExprRequirement(SubstDiag, IsSimple, + NoexceptLoc, std::move(Ret)); } concepts::ExprRequirement * RebuildExprRequirement(Expr *E, bool IsSimple, SourceLocation NoexceptLoc, concepts::ExprRequirement::ReturnTypeRequirement Ret) { - return SemaRef.BuildExprRequirement(E, IsSimple, NoexceptLoc, - std::move(Ret)); + return SemaRef.Concept().BuildExprRequirement(E, IsSimple, NoexceptLoc, + std::move(Ret)); } concepts::NestedRequirement * RebuildNestedRequirement(StringRef InvalidConstraintEntity, const ASTConstraintSatisfaction &Satisfaction) { - return SemaRef.BuildNestedRequirement(InvalidConstraintEntity, - Satisfaction); + return SemaRef.Concept().BuildNestedRequirement(InvalidConstraintEntity, + Satisfaction); } concepts::NestedRequirement *RebuildNestedRequirement(Expr *Constraint) { - return SemaRef.BuildNestedRequirement(Constraint); + return SemaRef.Concept().BuildNestedRequirement(Constraint); } /// \brief Build a new Objective-C boxed expression.