diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -345,6 +345,10 @@ `Issue 50455 `_, `Issue 54872 `_, `Issue 54587 `_. +- Clang now correctly delays the instantiation of function constraints until + the time of checking, which should now allow the libstdc++ ranges implementation + to work for at least trivial examples. This fixes + `Issue 44178 `_. C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2669,6 +2669,11 @@ /// template. bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y) const; + /// Determine whether two Friend functions are different because constraints + /// that refer to an enclosing template, according to [temp.friend] p9. + bool FriendsDifferByConstraints(const FunctionDecl *X, + const FunctionDecl *Y) const; + /// Determine whether the two declarations refer to the same entity. bool isSameEntity(const NamedDecl *X, const NamedDecl *Y) const; diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2474,6 +2474,19 @@ getCanonicalDecl()->FunctionDeclBits.IsMultiVersion = V; } + // Sets that this is a constrained friend where the constraint refers to an + // enclosing template. + void setFriendConstraintRefersToEnclosingTemplate(bool V = true) { + getCanonicalDecl() + ->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = V; + } + // Indicates this function is a constrained friend, where the constraint + // refers to an enclosing template for hte purposes of [temp.friend]p9. + bool FriendConstraintRefersToEnclosingTemplate() const { + return getCanonicalDecl() + ->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate; + } + /// Gets the kind of multiversioning attribute this declaration has. Note that /// this can return a value even if the function is not multiversion, such as /// the case of 'target'. diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1664,10 +1664,14 @@ /// Indicates if the function uses Floating Point Constrained Intrinsics uint64_t UsesFPIntrin : 1; + + // Indicates this function is a constrained friend, where the constraint + // refers to an enclosing template for hte purposes of [temp.friend]p9. + uint64_t FriendConstraintRefersToEnclosingTemplate : 1; }; /// Number of non-inherited bits in FunctionDeclBitfields. - enum { NumFunctionDeclBits = 28 }; + enum { NumFunctionDeclBits = 29 }; /// Stores the bits used by CXXConstructorDecl. If modified /// NumCXXConstructorDeclBits and the accessor @@ -1679,12 +1683,12 @@ /// For the bits in FunctionDeclBitfields. uint64_t : NumFunctionDeclBits; - /// 23 bits to fit in the remaining available space. + /// 22 bits to fit in the remaining available space. /// Note that this makes CXXConstructorDeclBitfields take /// exactly 64 bits and thus the width of NumCtorInitializers /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. - uint64_t NumCtorInitializers : 20; + uint64_t NumCtorInitializers : 19; uint64_t IsInheritingConstructor : 1; /// Whether this constructor has a trail-allocated explicit specifier. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3673,6 +3673,31 @@ bool ConsiderCudaAttrs = true, bool ConsiderRequiresClauses = true); + // Calculates whether the expression Constraint depends on an enclosing + // template, for the purposes of [temp.friend] p9. + // TemplateDepth is the 'depth' of the friend function, which is used to + // compare whether a declaration reference is referring to a containing + // template, or just the current friend function. A 'lower' TemplateDepth in + // the AST refers to a 'containing' template. As the constraint is + // uninstantiated, this is relative to the 'top' of the TU. + bool ConstraintExpressionDependsOnEnclosingTemplate(unsigned TemplateDepth, + const Expr *Constraint); + + // Calculates whether the friend function depends on an enclosing template for + // the purposes of [temp.friend] p9. + bool FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD); + + // 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 NamedDecl *New, + const Expr *NewConstr); + enum class AllowedExplicit { /// Allow no explicit functions to be used. None, @@ -7152,6 +7177,21 @@ 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, llvm::Optional> TemplateArgs, + MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope); + + /// Used during constraint checking, sets up the constraint template arguemnt + /// lists, and calls SetupConstraintScope to set up the + /// LocalInstantiationScope to have the proper set of ParVarDecls configured. + llvm::Optional + SetupConstraintCheckingTemplateArgumentsAndScope( + FunctionDecl *FD, llvm::Optional> TemplateArgs, + LocalInstantiationScope &Scope); + public: const NormalizedConstraint * getNormalizedAssociatedConstraints( @@ -7194,6 +7234,39 @@ 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 @@ -7213,8 +7286,8 @@ /// \returns true if an error occurred, false otherwise. bool CheckFunctionConstraints(const FunctionDecl *FD, ConstraintSatisfaction &Satisfaction, - SourceLocation UsageLoc = SourceLocation()); - + SourceLocation UsageLoc = SourceLocation(), + bool ForOverloadResolution = false); /// \brief Ensure that the given template arguments satisfy the constraints /// associated with the given template, emitting a diagnostic if they do not. @@ -8222,12 +8295,19 @@ TPL_TemplateTemplateArgumentMatch }; - bool TemplateParameterListsAreEqual(TemplateParameterList *New, - TemplateParameterList *Old, - bool Complain, - TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc - = SourceLocation()); + bool TemplateParameterListsAreEqual( + const NamedDecl *NewInstFrom, TemplateParameterList *New, + const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain, + TemplateParameterListEqualKind Kind, + SourceLocation TemplateArgLoc = SourceLocation()); + + bool TemplateParameterListsAreEqual( + TemplateParameterList *New, TemplateParameterList *Old, bool Complain, + TemplateParameterListEqualKind Kind, + SourceLocation TemplateArgLoc = SourceLocation()) { + return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain, + Kind, TemplateArgLoc); + } bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams); @@ -8962,7 +9042,8 @@ MultiLevelTemplateArgumentList getTemplateInstantiationArgs( const NamedDecl *D, const TemplateArgumentList *Innermost = nullptr, - bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr); + bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr, + bool LookBeyondLambda = false, bool IncludeContainingStruct = false); /// A context in which code is being synthesized (where a source location /// alone is not sufficient to identify the context). This covers template @@ -9670,23 +9751,21 @@ const MultiLevelTemplateArgumentList &TemplateArgs, SourceLocation Loc, DeclarationName Entity); - TypeSourceInfo *SubstFunctionDeclType(TypeSourceInfo *T, - const MultiLevelTemplateArgumentList &TemplateArgs, - SourceLocation Loc, - DeclarationName Entity, - CXXRecordDecl *ThisContext, - Qualifiers ThisTypeQuals); + TypeSourceInfo *SubstFunctionDeclType( + TypeSourceInfo *T, const MultiLevelTemplateArgumentList &TemplateArgs, + SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext, + Qualifiers ThisTypeQuals, bool EvaluateConstraints = true); void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto, const MultiLevelTemplateArgumentList &Args); bool SubstExceptionSpec(SourceLocation Loc, FunctionProtoType::ExceptionSpecInfo &ESI, SmallVectorImpl &ExceptionStorage, const MultiLevelTemplateArgumentList &Args); - ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D, - const MultiLevelTemplateArgumentList &TemplateArgs, - int indexAdjustment, - Optional NumExpansions, - bool ExpectParameterPack); + ParmVarDecl * + SubstParmVarDecl(ParmVarDecl *D, + const MultiLevelTemplateArgumentList &TemplateArgs, + int indexAdjustment, Optional NumExpansions, + bool ExpectParameterPack, bool EvaluateConstraints = true); bool SubstParmTypes(SourceLocation Loc, ArrayRef Params, const FunctionProtoType::ExtParameterInfo *ExtParamInfos, const MultiLevelTemplateArgumentList &TemplateArgs, @@ -9696,6 +9775,25 @@ ExprResult SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); + // A RAII type used by the TemplateDeclInstantiator and TemplateInstantiator + // to disable constraint evaluation, then restore the state. + template struct ConstraintEvalRAII { + InstTy &TI; + bool OldValue; + + ConstraintEvalRAII(InstTy &TI) + : TI(TI), OldValue(TI.getEvaluateConstraints()) { + TI.setEvaluateConstraints(false); + } + ~ConstraintEvalRAII() { TI.setEvaluateConstraints(OldValue); } + }; + + // Unlike the above, this evaluates constraints, which should only happen at + // 'constraint checking' time. + ExprResult + SubstConstraintExpr(Expr *E, + const MultiLevelTemplateArgumentList &TemplateArgs); + /// Substitute the given template arguments into a list of /// expressions, expanding pack expansions if required. /// @@ -9725,7 +9823,6 @@ const MultiLevelTemplateArgumentList &TemplateArgs, TemplateArgumentListInfo &Outputs); - Decl *SubstDecl(Decl *D, DeclContext *Owner, const MultiLevelTemplateArgumentList &TemplateArgs); @@ -9816,7 +9913,8 @@ const MultiLevelTemplateArgumentList &TemplateArgs); bool SubstTypeConstraint(TemplateTypeParmDecl *Inst, const TypeConstraint *TC, - const MultiLevelTemplateArgumentList &TemplateArgs); + const MultiLevelTemplateArgumentList &TemplateArgs, + bool EvaluateConstraint); bool InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD, ParmVarDecl *Param); diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -503,6 +503,7 @@ const MultiLevelTemplateArgumentList &TemplateArgs; Sema::LateInstantiatedAttrVec* LateAttrs = nullptr; LocalInstantiationScope *StartingScope = nullptr; + bool EvaluateConstraints = true; /// A list of out-of-line class template partial /// specializations that will need to be instantiated after the @@ -526,6 +527,13 @@ SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex), Owner(Owner), TemplateArgs(TemplateArgs) {} + void setEvaluateConstraints(bool B) { + EvaluateConstraints = B; + } + bool getEvaluateConstraints() { + return EvaluateConstraints; + } + // Define all the decl visitors using DeclNodes.inc #define DECL(DERIVED, BASE) \ Decl *Visit ## DERIVED ## Decl(DERIVED ## Decl *D); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6452,6 +6452,31 @@ return true; } +bool ASTContext::FriendsDifferByConstraints(const FunctionDecl *X, + const FunctionDecl *Y) const { + // If these aren't friends, then they aren't friends that differ by + // constraints. + if (!X->getFriendObjectKind() || !Y->getFriendObjectKind()) + return false; + + // If the the two functions share lexical declaration context, they are not in + // separate instantations, and thus in the same scope. + if (X->getLexicalDeclContext() == Y->getLexicalDeclContext()) + return false; + + if (!X->getDescribedFunctionTemplate()) { + assert(!Y->getDescribedFunctionTemplate() && + "How would these be the same if they aren't both templates?"); + + // If these friends don't have constraints, they aren't constrained, and + // thus don't fall under temp.friend p9. Else the simple presence of a + // constraint makes them unique. + return X->getTrailingRequiresClause(); + } + + return X->FriendConstraintRefersToEnclosingTemplate(); +} + bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const { if (X == Y) return true; @@ -6532,6 +6557,10 @@ FuncY->getTrailingRequiresClause())) return false; + // Constrained friends are different in certain cases, see: [temp.friend]p9. + if (FriendsDifferByConstraints(FuncX, FuncY)) + return false; + auto GetTypeAsWritten = [](const FunctionDecl *FD) { // Map to the first declaration that we've already merged into this one. // The TSI of redeclarations might not match (due to calling conventions diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3701,6 +3701,8 @@ ToFunction->setDefaulted(D->isDefaulted()); ToFunction->setExplicitlyDefaulted(D->isExplicitlyDefaulted()); ToFunction->setDeletedAsWritten(D->isDeletedAsWritten()); + ToFunction->setFriendConstraintRefersToEnclosingTemplate( + D->FriendConstraintRefersToEnclosingTemplate()); ToFunction->setRangeEnd(ToEndLoc); // Set the parameters. diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2970,6 +2970,7 @@ FunctionDeclBits.IsMultiVersion = false; FunctionDeclBits.IsCopyDeductionCandidate = false; FunctionDeclBits.HasODRHash = false; + FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false; if (TrailingRequiresClause) setTrailingRequiresClause(TrailingRequiresClause); } diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" #include "clang/Sema/SemaConcept.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaInternal.h" @@ -18,6 +19,7 @@ #include "clang/Sema/Template.h" #include "clang/Sema/Overload.h" #include "clang/Sema/Initialization.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/ExprConcepts.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/OperatorPrecedence.h" @@ -30,6 +32,7 @@ namespace { class LogicalBinOp { + SourceLocation Loc; OverloadedOperatorKind Op = OO_None; const Expr *LHS = nullptr; const Expr *RHS = nullptr; @@ -40,12 +43,14 @@ Op = BinaryOperator::getOverloadedOperator(BO->getOpcode()); LHS = BO->getLHS(); RHS = BO->getRHS(); + Loc = BO->getExprLoc(); } else if (auto *OO = dyn_cast(E)) { // If OO is not || or && it might not have exactly 2 arguments. if (OO->getNumArgs() == 2) { Op = OO->getOperator(); LHS = OO->getArg(0); RHS = OO->getArg(1); + Loc = OO->getOperatorLoc(); } } } @@ -56,6 +61,26 @@ const Expr *getLHS() const { return LHS; } const Expr *getRHS() const { return RHS; } + + ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS) const { + return recreateBinOp(SemaRef, LHS, const_cast(getRHS())); + } + + ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS, + ExprResult RHS) const { + assert((isAnd() || isOr()) && "Not the right kind of op?"); + assert((!LHS.isInvalid() && !RHS.isInvalid()) && "not good expressions?"); + + if (!LHS.isUsable() || !RHS.isUsable()) + return ExprEmpty(); + + // We should just be able to 'normalize' these to the builtin Binary + // Operator, since that is how they are evaluated in constriant checks. + return BinaryOperator::Create(SemaRef.Context, LHS.get(), RHS.get(), + BinaryOperator::getOverloadedOpcode(Op), + SemaRef.Context.BoolTy, VK_PRValue, + OK_Ordinary, Loc, FPOptionsOverride{}); + } }; } @@ -122,16 +147,18 @@ } template -static bool +static ExprResult calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction, AtomicEvaluator &&Evaluator) { ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts(); if (LogicalBinOp BO = ConstraintExpr) { - if (calculateConstraintSatisfaction(S, BO.getLHS(), Satisfaction, - Evaluator)) - return true; + ExprResult LHSRes = calculateConstraintSatisfaction( + S, BO.getLHS(), Satisfaction, Evaluator); + + if (LHSRes.isInvalid()) + return ExprError(); bool IsLHSSatisfied = Satisfaction.IsSatisfied; @@ -142,7 +169,7 @@ // is checked. If that is satisfied, the disjunction is satisfied. // Otherwise, the disjunction is satisfied if and only if the second // operand is satisfied. - return false; + return BO.recreateBinOp(S, LHSRes); if (BO.isAnd() && !IsLHSSatisfied) // [temp.constr.op] p2 @@ -151,12 +178,21 @@ // is checked. If that is not satisfied, the conjunction is not // satisfied. Otherwise, the conjunction is satisfied if and only if // the second operand is satisfied. - return false; + return BO.recreateBinOp(S, LHSRes); - return calculateConstraintSatisfaction( + ExprResult RHSRes = calculateConstraintSatisfaction( S, BO.getRHS(), Satisfaction, std::forward(Evaluator)); - } else if (auto *C = dyn_cast(ConstraintExpr)) { - return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction, + if (RHSRes.isInvalid()) + return ExprError(); + + return BO.recreateBinOp(S, LHSRes, RHSRes); + } + + if (auto *C = dyn_cast(ConstraintExpr)) { + // These aren't evaluated, so we don't care about cleanups, so we can just + // evaluate these as if the cleanups didn't exist. + return calculateConstraintSatisfaction( + S, C->getSubExpr(), Satisfaction, std::forward(Evaluator)); } @@ -164,11 +200,11 @@ ExprResult SubstitutedAtomicExpr = Evaluator(ConstraintExpr); if (SubstitutedAtomicExpr.isInvalid()) - return true; + return ExprError(); if (!SubstitutedAtomicExpr.isUsable()) // Evaluator has decided satisfaction without yielding an expression. - return false; + return ExprEmpty(); EnterExpressionEvaluationContext ConstantEvaluated( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); @@ -185,7 +221,7 @@ << SubstitutedAtomicExpr.get()->getSourceRange(); for (const PartialDiagnosticAt &PDiag : EvaluationDiags) S.Diag(PDiag.first, PDiag.second); - return true; + return ExprError(); } assert(EvalResult.Val.isInt() && @@ -195,10 +231,10 @@ Satisfaction.Details.emplace_back(ConstraintExpr, SubstitutedAtomicExpr.get()); - return false; + return SubstitutedAtomicExpr; } -static bool calculateConstraintSatisfaction( +static ExprResult calculateConstraintSatisfaction( Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { @@ -219,8 +255,8 @@ return ExprError(); // We do not want error diagnostics escaping here. Sema::SFINAETrap Trap(S); - SubstitutedExpression = S.SubstExpr(const_cast(AtomicExpr), - MLTAL); + SubstitutedExpression = + S.SubstConstraintExpr(const_cast(AtomicExpr), MLTAL); // Substitution might have stripped off a contextual conversion to // bool if this is the operand of an '&&' or '||'. For example, we // might lose an lvalue-to-rvalue conversion here. If so, put it back @@ -270,6 +306,7 @@ static bool CheckConstraintSatisfaction( Sema &S, const NamedDecl *Template, ArrayRef ConstraintExprs, + llvm::SmallVectorImpl &Converted, const MultiLevelTemplateArgumentList &TemplateArgsLists, SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { if (ConstraintExprs.empty()) { @@ -294,22 +331,30 @@ return true; for (const Expr *ConstraintExpr : ConstraintExprs) { - if (calculateConstraintSatisfaction(S, Template, TemplateIDRange.getBegin(), - TemplateArgsLists, ConstraintExpr, - Satisfaction)) + ExprResult Res = calculateConstraintSatisfaction( + S, Template, TemplateIDRange.getBegin(), TemplateArgsLists, + ConstraintExpr, Satisfaction); + if (Res.isInvalid()) return true; - if (!Satisfaction.IsSatisfied) + + Converted.push_back(Res.get()); + if (!Satisfaction.IsSatisfied) { + // Backfill the 'converted' list with nulls so we can keep the Converted + // and unconverted lists in sync. + Converted.append(ConstraintExprs.size() - Converted.size(), nullptr); // [temp.constr.op] p2 - // [...] To determine if a conjunction is satisfied, the satisfaction - // of the first operand is checked. If that is not satisfied, the - // conjunction is not satisfied. [...] + // [...] To determine if a conjunction is satisfied, the satisfaction + // of the first operand is checked. If that is not satisfied, the + // conjunction is not satisfied. [...] return false; + } } return false; } bool Sema::CheckConstraintSatisfaction( const NamedDecl *Template, ArrayRef ConstraintExprs, + llvm::SmallVectorImpl &ConvertedConstraints, const MultiLevelTemplateArgumentList &TemplateArgsLists, SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { if (ConstraintExprs.empty()) { @@ -317,9 +362,9 @@ return false; } if (!Template) { - return ::CheckConstraintSatisfaction(*this, nullptr, ConstraintExprs, - TemplateArgsLists, TemplateIDRange, - OutSatisfaction); + return ::CheckConstraintSatisfaction( + *this, nullptr, ConstraintExprs, ConvertedConstraints, + TemplateArgsLists, TemplateIDRange, OutSatisfaction); } // A list of the template argument list flattened in a predictible manner for @@ -340,8 +385,8 @@ auto Satisfaction = std::make_unique(Template, FlattenedArgs); if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, - TemplateArgsLists, TemplateIDRange, - *Satisfaction)) { + ConvertedConstraints, TemplateArgsLists, + TemplateIDRange, *Satisfaction)) { return true; } OutSatisfaction = *Satisfaction; @@ -355,21 +400,120 @@ bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { return calculateConstraintSatisfaction( - *this, ConstraintExpr, Satisfaction, - [this](const Expr *AtomicExpr) -> ExprResult { - // We only do this to immitate lvalue-to-rvalue conversion. - return PerformContextuallyConvertToBool(const_cast(AtomicExpr)); - }); + *this, ConstraintExpr, Satisfaction, + [this](const Expr *AtomicExpr) -> ExprResult { + // We only do this to immitate lvalue-to-rvalue conversion. + return PerformContextuallyConvertToBool( + const_cast(AtomicExpr)); + }) + .isInvalid(); +} + +bool Sema::SetupConstraintScope( + FunctionDecl *FD, llvm::Optional> TemplateArgs, + MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope) { + if (FD->isTemplateInstantiation() && FD->getPrimaryTemplate()) { + FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate(); + InstantiatingTemplate Inst( + *this, FD->getPointOfInstantiation(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, PrimaryTemplate, + TemplateArgs ? *TemplateArgs : ArrayRef{}, + SourceRange()); + if (Inst.isInvalid()) + return true; + + // addInstantiatedParametersToScope creates a map of 'uninstantiated' to + // 'instantiated' parameters and adds it to the context. For the case where + // this function is a template being instantiated NOW, we also need to add + // the list of current template arguments to the list so that they also can + // be picked out of the map. + if (auto *SpecArgs = FD->getTemplateSpecializationArgs()) { + MultiLevelTemplateArgumentList JustTemplArgs(*SpecArgs); + if (addInstantiatedParametersToScope( + FD, PrimaryTemplate->getTemplatedDecl(), Scope, JustTemplArgs)) + return true; + } + + // If this is a member function, make sure we get the parameters that + // reference the original primary template. + if (const auto *FromMemTempl = + PrimaryTemplate->getInstantiatedFromMemberTemplate()) { + if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(), + Scope, MLTAL)) + return true; + } + + return false; + } + + if (FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization || + FD->getTemplatedKind() == FunctionDecl::TK_DependentNonTemplate) { + FunctionDecl *InstantiatedFrom = + FD->getTemplatedKind() == FunctionDecl::TK_MemberSpecialization + ? FD->getInstantiatedFromMemberFunction() + : FD->getInstantiatedFromDecl(); + + InstantiatingTemplate Inst( + *this, FD->getPointOfInstantiation(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, InstantiatedFrom, + TemplateArgs ? *TemplateArgs : ArrayRef{}, + SourceRange()); + if (Inst.isInvalid()) + return true; + + // Case where this was not a template, but instantiated as a + // child-function. + if (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. +llvm::Optional +Sema::SetupConstraintCheckingTemplateArgumentsAndScope( + FunctionDecl *FD, llvm::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, nullptr, /*RelativeToPrimary*/ true, + /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true); + if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope)) + return {}; + + return MLTAL; } bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, ConstraintSatisfaction &Satisfaction, - SourceLocation UsageLoc) { - const Expr *RC = FD->getTrailingRequiresClause(); - if (RC->isInstantiationDependent()) { + 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 + // better. + if (FD->isDependentContext() || + FD->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) { Satisfaction.IsSatisfied = true; return false; } + + ContextRAII SavedContext{ + *this, cast( + const_cast(FD)->getNonClosureContext())}; + LocalInstantiationScope Scope(*this, !ForOverloadResolution || + isLambdaCallOperator(FD)); + llvm::Optional MLTAL = + SetupConstraintCheckingTemplateArgumentsAndScope( + const_cast(FD), {}, Scope); + Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast(FD)) { @@ -380,10 +524,112 @@ // We substitute with empty arguments in order to rebuild the atomic // constraint in a constant-evaluated context. // FIXME: Should this be a dedicated TreeTransform? - return CheckConstraintSatisfaction( - FD, {RC}, /*TemplateArgs=*/{}, - SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), - Satisfaction); + const Expr *RC = FD->getTrailingRequiresClause(); + llvm::SmallVector Converted; + + if (CheckConstraintSatisfaction( + FD, {RC}, Converted, *MLTAL, + SourceRange(UsageLoc.isValid() ? UsageLoc : FD->getLocation()), + Satisfaction)) + return true; + + // FIXME: we need to do this for the function constraints for + // comparison of constraints to work, but do we also need to do it for + // CheckInstantiatedFunctionConstraints? That one is more difficult, but we + // seem to always just pick up the constraints from the primary template. + assert(Converted.size() <= 1 && "Got more expressions converted?"); + if (!Converted.empty() && Converted[0] != nullptr) + const_cast(FD)->setTrailingRequiresClause(Converted[0]); + return false; +} + + +// Figure out the to-translation-unit depth for this function declaration for +// the purpose of seeing if they differ by constraints. This isn't the same as +// getTemplateDepth, because it includes already instantiated parents. +static unsigned CalculateTemplateDepthForConstraints(Sema &S, + const NamedDecl *ND) { + MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( + ND, nullptr, /*RelativeToPrimary*/ true, + /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true); + return MLTAL.getNumSubstitutedLevels(); +} + +namespace { + class AdjustConstraintDepth : public TreeTransform { + unsigned TemplateDepth = 0; + public: + using inherited = TreeTransform; + AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth) + : inherited(SemaRef), TemplateDepth(TemplateDepth) {} + QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB, + TemplateTypeParmTypeLoc TL) { + const TemplateTypeParmType *T = TL.getTypePtr(); + + TemplateTypeParmDecl *NewTTPDecl = nullptr; + if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl()) + NewTTPDecl = cast_or_null( + TransformDecl(TL.getNameLoc(), OldTTPDecl)); + + QualType Result = getSema().Context.getTemplateTypeParmType( + T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(), + NewTTPDecl); + TemplateTypeParmTypeLoc NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + return Result; + } + }; +} // namespace + +bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old, + const Expr *OldConstr, + const NamedDecl *New, + const Expr *NewConstr) { + if (Old && New && Old != New) { + unsigned Depth1 = CalculateTemplateDepthForConstraints( + *this, Old); + unsigned Depth2 = CalculateTemplateDepthForConstraints( + *this, New); + + // Adjust the 'shallowest' verison of this to increase the depth to match + // the 'other'. + if (Depth2 > Depth1) { + OldConstr = AdjustConstraintDepth(*this, Depth2 - Depth1) + .TransformExpr(const_cast(OldConstr)) + .get(); + } else if (Depth1 > Depth2) { + NewConstr = AdjustConstraintDepth(*this, Depth1 - Depth2) + .TransformExpr(const_cast(NewConstr)) + .get(); + } + } + + llvm::FoldingSetNodeID ID1, ID2; + OldConstr->Profile(ID1, Context, /*Canonical=*/true); + NewConstr->Profile(ID2, Context, /*Canonical=*/true); + return ID1 == ID2; +} + +bool Sema::FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD) { + assert(FD->getFriendObjectKind() && "Must be a friend!"); + + // The logic for non-templates is handled in ASTContext::isSameEntity, so we + // don't have to bother checking 'DependsOnEnclosingTemplate' for a + // non-function-template. + assert(FD->getDescribedFunctionTemplate() && + "Non-function templates don't need to be checked"); + + SmallVector ACs; + FD->getDescribedFunctionTemplate()->getAssociatedConstraints(ACs); + + unsigned OldTemplateDepth = CalculateTemplateDepthForConstraints(*this, FD); + for (const Expr *Constraint : ACs) + if (ConstraintExpressionDependsOnEnclosingTemplate(OldTemplateDepth, + Constraint)) + return true; + + return false; } bool Sema::EnsureTemplateArgumentListConstraints( @@ -432,26 +678,14 @@ // PushDeclContext because we don't have a scope. Sema::ContextRAII savedContext(*this, Decl); LocalInstantiationScope Scope(*this); - MultiLevelTemplateArgumentList MLTAL; - // FIXME: This will be replaced with some logic to get all the template - // arguments when we switch to deferred template instantiation. - MLTAL.addOuterTemplateArguments(TemplateArgs); - - // If this is not an explicit specialization - we need to get the instantiated - // version of the template arguments and add them to scope for the - // substitution. - if (Decl->isTemplateInstantiation()) { - InstantiatingTemplate Inst(*this, Decl->getPointOfInstantiation(), - InstantiatingTemplate::ConstraintsCheck{}, Decl->getPrimaryTemplate(), - TemplateArgs, SourceRange()); - if (Inst.isInvalid()) - return true; - MultiLevelTemplateArgumentList MLTAL( - *Decl->getTemplateSpecializationArgs()); - if (addInstantiatedParametersToScope( - Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), Scope, MLTAL)) - return true; - } + + Optional MLTAL = + SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs, + Scope); + + if (!MLTAL) + return true; + Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast(Decl)) { @@ -459,7 +693,8 @@ Record = Method->getParent(); } CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); - return CheckConstraintSatisfaction(Template, TemplateAC, MLTAL, + llvm::SmallVector Converted; + return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, PointOfInstantiation, Satisfaction); } @@ -734,22 +969,22 @@ return CacheEntry->second; } -static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, - ConceptDecl *Concept, ArrayRef TemplateArgs, - const ASTTemplateArgumentListInfo *ArgsAsWritten) { +static bool +substituteParameterMappings(Sema &S, NormalizedConstraint &N, + ConceptDecl *Concept, + const MultiLevelTemplateArgumentList &MLTAL, + const ASTTemplateArgumentListInfo *ArgsAsWritten) { if (!N.isAtomic()) { - if (substituteParameterMappings(S, N.getLHS(), Concept, TemplateArgs, + if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL, ArgsAsWritten)) return true; - return substituteParameterMappings(S, N.getRHS(), Concept, TemplateArgs, + return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL, ArgsAsWritten); } TemplateParameterList *TemplateParams = Concept->getTemplateParameters(); AtomicConstraint &Atomic = *N.getAtomicConstraint(); TemplateArgumentListInfo SubstArgs; - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(TemplateArgs); if (!Atomic.ParameterMapping) { llvm::SmallBitVector OccurringIndices(TemplateParams->size()); S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false, @@ -790,6 +1025,20 @@ return false; } +static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N, + const ConceptSpecializationExpr *CSE) { + TemplateArgumentList TAL{TemplateArgumentList::OnStack, + CSE->getTemplateArguments()}; + MultiLevelTemplateArgumentList MLTAL = + S.getTemplateInstantiationArgs(CSE->getNamedConcept(), &TAL, + /*RelativeToPrimary*/ true, + /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true); + + return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL, + CSE->getTemplateArgsAsWritten()); +} + Optional NormalizedConstraint::fromConstraintExprs(Sema &S, NamedDecl *D, ArrayRef E) { @@ -852,9 +1101,7 @@ Optional New; New.emplace(S.Context, *SubNF); - if (substituteParameterMappings( - S, *New, CSE->getNamedConcept(), - CSE->getTemplateArguments(), CSE->getTemplateArgsAsWritten())) + if (substituteParameterMappings(S, *New, CSE)) return None; return New; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10480,6 +10480,12 @@ } if (getLangOpts().CPlusPlus) { + // 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)) + NewFD->setFriendConstraintRefersToEnclosingTemplate(true); + if (FunctionTemplate) { if (NewFD->isInvalidDecl()) FunctionTemplate->setInvalidDecl(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -283,7 +283,8 @@ // definition. if (FD->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(FD, Satisfaction, Loc)) + if (CheckFunctionConstraints(FD, Satisfaction, Loc, + /*ForOverloadResolution*/ true)) // A diagnostic will have already been generated (non-constant // constraint expression, for example) return true; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1074,6 +1074,15 @@ !shouldLinkPossiblyHiddenDecl(*I, New)) continue; + // C++20 [temp.friend] p9: A non-template friend declaration with a + // requires-clause shall be a definition. A friend function template + // with a constraint that depends on a template parameter from an + // enclosing template shall be a definition. Such a constrained friend + // function or function template declaration does not declare the same + // function or function template as a declaration in any other scope. + if (Context.FriendsDifferByConstraints(OldF, New)) + continue; + Match = *I; return Ovl_Match; } @@ -6521,7 +6530,8 @@ if (Function->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(Function, Satisfaction) || + if (CheckFunctionConstraints(Function, Satisfaction, /*Loc*/ {}, + /*ForOverloadResolution*/ true) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; @@ -7027,7 +7037,8 @@ if (Method->getTrailingRequiresClause()) { ConstraintSatisfaction Satisfaction; - if (CheckFunctionConstraints(Method, Satisfaction) || + if (CheckFunctionConstraints(Method, Satisfaction, /*Loc*/ {}, + /*ForOverloadResolution*/ true) || !Satisfaction.IsSatisfied) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_constraints_not_satisfied; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1687,6 +1687,61 @@ return Param; } +namespace { +class ConstraintRefersToContainingTemplateChecker + : public TreeTransform { + bool Result = false; + unsigned TemplateDepth = 0; + +public: + using inherited = TreeTransform; + + ConstraintRefersToContainingTemplateChecker(Sema &SemaRef, + unsigned TemplateDepth) + : inherited(SemaRef), TemplateDepth(TemplateDepth) {} + bool getResult() const { return Result; } + + // This should be the only template parm type that we have to deal with. + // SubstTempalteTypeParmPack, SubstNonTypeTemplateParmPack, and + // FunctionParmPackExpr are all partially substituted, which cannot happen + // with concepts at this point in translation. + QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB, + TemplateTypeParmTypeLoc TL) { + assert(TL.getDecl()->getDepth() <= TemplateDepth && + "Nothing should reference a value below the actual template depth, " + "depth is likely wrong"); + if (TL.getDecl()->getDepth() != TemplateDepth) + Result = true; + return inherited::TransformTemplateTypeParmType(TLB, TL); + } + + Decl *TransformDecl(SourceLocation Loc, Decl *D) { + // FIXME : This is possibly an incomplete list, but it is unclear what other + // Decl kinds could be used to refer to the template parameters. This is a + // best guess so far based on examples currently available, but the + // unreachable should catch future instances/cases. + if (auto *TD = dyn_cast(D)) + TransformType(TD->getUnderlyingType()); + else if (auto *VD = dyn_cast(D)) + TransformType(VD->getType()); + else if (auto *TD = dyn_cast(D)) + TransformTemplateParameterList(TD->getTemplateParameters()); + else if (isa(D)) { + // No direct types to visit here I believe. + } else + llvm_unreachable("Don't know how to handle this declaration type yet"); + return D; + } +}; +} // namespace + +bool Sema::ConstraintExpressionDependsOnEnclosingTemplate( + unsigned TemplateDepth, const Expr *Constraint) { + ConstraintRefersToContainingTemplateChecker Checker(*this, TemplateDepth); + Checker.TransformExpr(const_cast(Constraint)); + return Checker.getResult(); +} + /// ActOnTemplateParameterList - Builds a TemplateParameterList, optionally /// constrained by RequiresClause, that contains the template parameters in /// Params. @@ -2288,7 +2343,8 @@ TTP->isExpandedParameterPack() ? llvm::Optional(TTP->getNumExpansionParameters()) : None); if (const auto *TC = TTP->getTypeConstraint()) - SemaRef.SubstTypeConstraint(NewTTP, TC, Args); + SemaRef.SubstTypeConstraint(NewTTP, TC, Args, + /*EvaluateConstraint*/ true); if (TTP->hasDefaultArgument()) { TypeSourceInfo *InstantiatedDefaultArg = SemaRef.SubstType(TTP->getDefaultArgumentInfo(), Args, @@ -6014,10 +6070,30 @@ TemplateArgs = std::move(NewArgs); if (!PartialTemplateArgs) { - // FIXME: This will be changed a bit once deferred concept instantiation is - // implemented. - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(Converted); + TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack, + Converted); + // Setup the context/ThisScope for the case where we are needing to + // re-instantiate constraints outside of normal instantiation. + DeclContext *NewContext = Template->getDeclContext(); + + // If this template is in a template, make sure we extract the templated + // decl. + if (auto *TD = dyn_cast(NewContext)) + NewContext = Decl::castToDeclContext(TD->getTemplatedDecl()); + auto *RD = dyn_cast(NewContext); + + Qualifiers ThisQuals; + if (const auto *Method = + dyn_cast_or_null(Template->getTemplatedDecl())) + ThisQuals = Method->getMethodQualifiers(); + + ContextRAII Context(*this, NewContext); + CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr); + + MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( + Template, &StackTemplateArgs, /*RelativeToPrimary*/ true, + /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true); if (EnsureTemplateArgumentListConstraints( Template, MLTAL, SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { @@ -7564,7 +7640,9 @@ // are not considered. if (ParamsAC.empty()) return false; + Template->getAssociatedConstraints(TemplateAC); + bool IsParamAtLeastAsConstrained; if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, IsParamAtLeastAsConstrained)) @@ -7760,10 +7838,10 @@ } /// Match two template parameters within template parameter lists. -static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old, - bool Complain, - Sema::TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc) { +static bool MatchTemplateParameterKind( + Sema &S, NamedDecl *New, const NamedDecl *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()) { if (Complain) { @@ -7845,13 +7923,13 @@ else if (TemplateTemplateParmDecl *OldTTP = dyn_cast(Old)) { TemplateTemplateParmDecl *NewTTP = cast(New); - if (!S.TemplateParameterListsAreEqual(NewTTP->getTemplateParameters(), - OldTTP->getTemplateParameters(), - Complain, - (Kind == Sema::TPL_TemplateMatch - ? Sema::TPL_TemplateTemplateParmMatch - : Kind), - TemplateArgLoc)) + if (!S.TemplateParameterListsAreEqual( + NewInstFrom, NewTTP->getTemplateParameters(), OldInstFrom, + OldTTP->getTemplateParameters(), Complain, + (Kind == Sema::TPL_TemplateMatch + ? Sema::TPL_TemplateTemplateParmMatch + : Kind), + TemplateArgLoc)) return false; } else if (Kind != Sema::TPL_TemplateTemplateArgumentMatch) { const Expr *NewC = nullptr, *OldC = nullptr; @@ -7874,10 +7952,8 @@ } if (NewC) { - llvm::FoldingSetNodeID OldCID, NewCID; - OldC->Profile(OldCID, S.Context, /*Canonical=*/true); - NewC->Profile(NewCID, S.Context, /*Canonical=*/true); - if (OldCID != NewCID) { + if (!S.AreConstraintExpressionsEqual(OldInstFrom, OldC, NewInstFrom, + NewC)) { if (Complain) Diagnose(); return false; @@ -7933,12 +8009,10 @@ /// /// \returns True if the template parameter lists are equal, false /// otherwise. -bool -Sema::TemplateParameterListsAreEqual(TemplateParameterList *New, - TemplateParameterList *Old, - bool Complain, - TemplateParameterListEqualKind Kind, - SourceLocation TemplateArgLoc) { +bool Sema::TemplateParameterListsAreEqual( + const NamedDecl *NewInstFrom, TemplateParameterList *New, + const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain, + TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc) { if (Old->size() != New->size() && Kind != TPL_TemplateTemplateArgumentMatch) { if (Complain) DiagnoseTemplateParameterListArityMismatch(*this, New, Old, Kind, @@ -7968,8 +8042,9 @@ return false; } - if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, - Kind, TemplateArgLoc)) + if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm, + OldInstFrom, Complain, Kind, + TemplateArgLoc)) return false; ++NewParm; @@ -7984,8 +8059,9 @@ // template parameter pack in P (ignoring whether those template // parameters are template parameter packs). for (; NewParm != NewParmEnd; ++NewParm) { - if (!MatchTemplateParameterKind(*this, *NewParm, *OldParm, Complain, - Kind, TemplateArgLoc)) + if (!MatchTemplateParameterKind(*this, *NewParm, NewInstFrom, *OldParm, + OldInstFrom, Complain, Kind, + TemplateArgLoc)) return false; } } @@ -8017,10 +8093,8 @@ } if (NewRC) { - llvm::FoldingSetNodeID OldRCID, NewRCID; - OldRC->Profile(OldRCID, Context, /*Canonical=*/true); - NewRC->Profile(NewRCID, Context, /*Canonical=*/true); - if (OldRCID != NewRCID) { + if (!AreConstraintExpressionsEqual(OldInstFrom, OldRC, NewInstFrom, + NewRC)) { if (Complain) Diagnose(); return false; diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2848,6 +2848,20 @@ struct IsPartialSpecialization { static constexpr bool value = true; }; +template +static bool DeducedArgsNeedReplacement(TemplateDeclT *Template) { + return false; +} +template <> +bool DeducedArgsNeedReplacement( + VarTemplatePartialSpecializationDecl *Spec) { + return !Spec->isClassScopeExplicitSpecialization(); +} +template <> +bool DeducedArgsNeedReplacement( + ClassTemplatePartialSpecializationDecl *Spec) { + return !Spec->isClassScopeExplicitSpecialization(); +} template static Sema::TemplateDeductionResult @@ -2856,13 +2870,25 @@ TemplateDeductionInfo& Info) { llvm::SmallVector AssociatedConstraints; Template->getAssociatedConstraints(AssociatedConstraints); - // FIXME: This will change quite a bit once deferred concept instantiation is - // implemented. MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(DeducedArgs); - if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, - MLTAL, Info.getLocation(), + bool NeedsReplacement = DeducedArgsNeedReplacement(Template); + TemplateArgumentList DeducedTAL{TemplateArgumentList::OnStack, DeducedArgs}; + + MLTAL = S.getTemplateInstantiationArgs( + Template, /*InnerMost*/ NeedsReplacement ? nullptr : &DeducedTAL, + /*RelativeToPrimary*/ true, /*Pattern*/ + nullptr, /*LookBeyondLambda*/ true); + + // getTemplateInstantiationArgs picks up the non-deduced version of the + // template args when this is a variable template partial specialization and + // not class-scope explicit specialization, so replace with Deduced Args + // instead of adding to inner-most. + if (NeedsReplacement) + MLTAL.replaceInnermostTemplateArguments(DeducedArgs); + + if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, + Info.getLocation(), Info.AssociatedConstraintsSatisfaction) || !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs)); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -57,9 +57,18 @@ /// instantiating the definition of the given declaration, \p D. This is /// used to determine the proper set of template instantiation arguments for /// friend function template specializations. +/// +/// \param LookBeyondLambda Indicates that this collection of arguments should +/// continue looking when it encounters a lambda generic call operator. +/// +/// \param IncludeContainingStructArgs Indicates that this collection of +/// arguments should include arguments for any class template that this +/// declaration is included inside of. + MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( const NamedDecl *D, const TemplateArgumentList *Innermost, - bool RelativeToPrimary, const FunctionDecl *Pattern) { + bool RelativeToPrimary, const FunctionDecl *Pattern, bool LookBeyondLambda, + bool IncludeContainingStructArgs) { // Accumulate the set of template argument lists in this structure. MultiLevelTemplateArgumentList Result; @@ -155,11 +164,13 @@ break; // If this function is a generic lambda specialization, we are done. - if (isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) + if (!LookBeyondLambda && + isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) break; } else if (Function->getDescribedFunctionTemplate()) { - assert(Result.getNumSubstitutedLevels() == 0 && + assert((IncludeContainingStructArgs || + Result.getNumSubstitutedLevels() == 0) && "Outer template not instantiated?"); } @@ -176,10 +187,28 @@ } } else if (const auto *Rec = dyn_cast(Ctx)) { if (ClassTemplateDecl *ClassTemplate = Rec->getDescribedClassTemplate()) { - assert(Result.getNumSubstitutedLevels() == 0 && + assert((IncludeContainingStructArgs || + Result.getNumSubstitutedLevels() == 0) && "Outer template not instantiated?"); if (ClassTemplate->isMemberSpecialization()) break; + if (IncludeContainingStructArgs) { + QualType RecordType = Context.getTypeDeclType(Rec); + QualType Injected = cast(RecordType) + ->getInjectedSpecializationType(); + const auto *InjectedType = cast(Injected); + Result.addOuterTemplateArguments(InjectedType->template_arguments()); + } + } + bool IsFriend = Rec->getFriendObjectKind() || + (Rec->getDescribedClassTemplate() && + Rec->getDescribedClassTemplate()->getFriendObjectKind()); + if (IncludeContainingStructArgs && IsFriend && + Rec->getNonTransparentDeclContext()->isFileContext() && + (!Pattern || !Pattern->getLexicalDeclContext()->isFileContext())) { + Ctx = Rec->getLexicalDeclContext(); + RelativeToPrimary = false; + continue; } } @@ -930,16 +959,23 @@ const MultiLevelTemplateArgumentList &TemplateArgs; SourceLocation Loc; DeclarationName Entity; + bool EvaluateConstraints = true; public: typedef TreeTransform inherited; TemplateInstantiator(Sema &SemaRef, const MultiLevelTemplateArgumentList &TemplateArgs, - SourceLocation Loc, - DeclarationName Entity) - : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), - Entity(Entity) { } + SourceLocation Loc, DeclarationName Entity) + : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc), + Entity(Entity) {} + + void setEvaluateConstraints(bool B) { + EvaluateConstraints = B; + } + bool getEvaluateConstraints() { + return EvaluateConstraints; + } /// Determine whether the given type \p T has already been /// transformed. @@ -1146,7 +1182,9 @@ ExprResult TransformLambdaExpr(LambdaExpr *E) { LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); - return inherited::TransformLambdaExpr(E); + Sema::ConstraintEvalRAII RAII(*this); + ExprResult Res = inherited::TransformLambdaExpr(E); + return Res; } ExprResult TransformRequiresExpr(RequiresExpr *E) { @@ -1191,6 +1229,7 @@ DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext(); TemplateDeclInstantiator DeclInstantiator(getSema(), /* DeclContext *Owner */ Owner, TemplateArgs); + DeclInstantiator.setEvaluateConstraints(EvaluateConstraints); return DeclInstantiator.SubstTemplateParams(OrigTPL); } @@ -1766,9 +1805,9 @@ int indexAdjustment, Optional NumExpansions, bool ExpectParameterPack) { - auto NewParm = - SemaRef.SubstParmVarDecl(OldParm, TemplateArgs, indexAdjustment, - NumExpansions, ExpectParameterPack); + auto NewParm = SemaRef.SubstParmVarDecl( + OldParm, TemplateArgs, indexAdjustment, NumExpansions, + ExpectParameterPack, EvaluateConstraints); if (NewParm && SemaRef.getLangOpts().OpenCL) SemaRef.deduceOpenCLAddressSpace(NewParm); return NewParm; @@ -1988,8 +2027,7 @@ Req, Info, OrigTPL->getSourceRange()); if (TPLInst.isInvalid()) return nullptr; - TemplateParameterList *TPL = - TransformTemplateParameterList(OrigTPL); + TemplateParameterList *TPL = TransformTemplateParameterList(OrigTPL); if (!TPL) TransRetReq.emplace(createSubstDiag(SemaRef, Info, [&] (llvm::raw_ostream& OS) { @@ -2199,7 +2237,8 @@ SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext, - Qualifiers ThisTypeQuals) { + Qualifiers ThisTypeQuals, + bool EvaluateConstraints) { assert(!CodeSynthesisContexts.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -2208,6 +2247,7 @@ return T; TemplateInstantiator Instantiator(*this, Args, Loc, Entity); + Instantiator.setEvaluateConstraints(EvaluateConstraints); TypeLocBuilder TLB; @@ -2352,9 +2392,19 @@ bool Sema::SubstTypeConstraint( TemplateTypeParmDecl *Inst, const TypeConstraint *TC, - const MultiLevelTemplateArgumentList &TemplateArgs) { + const MultiLevelTemplateArgumentList &TemplateArgs, + bool EvaluateConstraints) { const ASTTemplateArgumentListInfo *TemplArgInfo = TC->getTemplateArgsAsWritten(); + + if (!EvaluateConstraints) { + Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), TC->getNamedConcept(), + TC->getNamedConcept(), TemplArgInfo, + TC->getImmediatelyDeclaredConstraint()); + return false; + } + TemplateArgumentListInfo InstArgs; if (TemplArgInfo) { @@ -2373,11 +2423,11 @@ : SourceLocation()); } -ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, - const MultiLevelTemplateArgumentList &TemplateArgs, - int indexAdjustment, - Optional NumExpansions, - bool ExpectParameterPack) { +ParmVarDecl * +Sema::SubstParmVarDecl(ParmVarDecl *OldParm, + const MultiLevelTemplateArgumentList &TemplateArgs, + int indexAdjustment, Optional NumExpansions, + bool ExpectParameterPack, bool EvaluateConstraint) { TypeSourceInfo *OldDI = OldParm->getTypeSourceInfo(); TypeSourceInfo *NewDI = nullptr; @@ -2435,9 +2485,7 @@ // template's described function, but we might also get here later. // Make sure we do not instantiate the TypeConstraint more than once. if (Inst && !Inst->getTypeConstraint()) { - // TODO: Concepts: do not instantiate the constraint (delayed constraint - // substitution) - if (SubstTypeConstraint(Inst, TC, TemplateArgs)) + if (SubstTypeConstraint(Inst, TC, TemplateArgs, EvaluateConstraint)) return nullptr; } } @@ -2746,6 +2794,7 @@ Instantiation->setInvalidDecl(); TemplateDeclInstantiator Instantiator(*this, Instantiation, TemplateArgs); + Instantiator.setEvaluateConstraints(false); SmallVector Fields; // Delay instantiation of late parsed attributes. LateInstantiatedAttrVec LateAttrs; @@ -3524,11 +3573,9 @@ ArrayRef Args, const MultiLevelTemplateArgumentList &TemplateArgs, TemplateArgumentListInfo &Out) { - TemplateInstantiator Instantiator(*this, TemplateArgs, - SourceLocation(), + TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(), DeclarationName()); - return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), - Out); + return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out); } ExprResult @@ -3542,11 +3589,23 @@ return Instantiator.TransformExpr(E); } +ExprResult +Sema::SubstConstraintExpr(Expr *E, + const MultiLevelTemplateArgumentList &TemplateArgs) { + if (!E) + return E; + + // This is where we need to make sure we 'know' constraint checking needs to + // happen. + TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(), + DeclarationName()); + return Instantiator.TransformExpr(E); +} + ExprResult Sema::SubstInitializer(Expr *Init, const MultiLevelTemplateArgumentList &TemplateArgs, bool CXXDirectInit) { - TemplateInstantiator Instantiator(*this, TemplateArgs, - SourceLocation(), + TemplateInstantiator Instantiator(*this, TemplateArgs, SourceLocation(), DeclarationName()); return Instantiator.TransformInitializer(Init, CXXDirectInit); } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1635,12 +1635,16 @@ } if (PrevClassTemplate) { - TemplateParameterList *PrevParams - = PrevClassTemplate->getMostRecentDecl()->getTemplateParameters(); + const ClassTemplateDecl *MostRecentPrevCT = + PrevClassTemplate->getMostRecentDecl(); + TemplateParameterList *PrevParams = + MostRecentPrevCT->getTemplateParameters(); // Make sure the parameter lists match. - if (!SemaRef.TemplateParameterListsAreEqual(InstParams, PrevParams, true, - Sema::TPL_TemplateMatch)) + if (!SemaRef.TemplateParameterListsAreEqual( + D->getTemplatedDecl(), InstParams, + MostRecentPrevCT->getTemplatedDecl(), PrevParams, true, + Sema::TPL_TemplateMatch)) return nullptr; // Do some additional validation, then merge default arguments @@ -1830,6 +1834,7 @@ // merged with the local instantiation scope for the function template // itself. LocalInstantiationScope Scope(SemaRef); + Sema::ConstraintEvalRAII RAII(*this); TemplateParameterList *TempParams = D->getTemplateParameters(); TemplateParameterList *InstParams = SubstTemplateParams(TempParams); @@ -2069,19 +2074,7 @@ return nullptr; } - // FIXME: Concepts: Do not substitute into constraint expressions Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); - if (TrailingRequiresClause) { - EnterExpressionEvaluationContext ConstantEvaluated( - SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); - ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, - TemplateArgs); - if (SubstRC.isInvalid()) - return nullptr; - TrailingRequiresClause = SubstRC.get(); - if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) - return nullptr; - } // If we're instantiating a local function declaration, put the result // in the enclosing namespace; otherwise we need to find the instantiated @@ -2121,6 +2114,8 @@ D->getCanonicalDecl()->getStorageClass(), D->UsesFPIntrin(), D->isInlineSpecified(), D->hasWrittenPrototype(), D->getConstexprKind(), TrailingRequiresClause); + Function->setFriendConstraintRefersToEnclosingTemplate( + D->FriendConstraintRefersToEnclosingTemplate()); Function->setRangeEnd(D->getSourceRange().getEnd()); } @@ -2432,23 +2427,6 @@ return nullptr; } - // FIXME: Concepts: Do not substitute into constraint expressions - Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); - if (TrailingRequiresClause) { - EnterExpressionEvaluationContext ConstantEvaluated( - SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); - auto *ThisContext = dyn_cast_or_null(Owner); - Sema::CXXThisScopeRAII ThisScope(SemaRef, ThisContext, - D->getMethodQualifiers(), ThisContext); - ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, - TemplateArgs); - if (SubstRC.isInvalid()) - return nullptr; - TrailingRequiresClause = SubstRC.get(); - if (!SemaRef.CheckConstraintExpression(TrailingRequiresClause)) - return nullptr; - } - DeclContext *DC = Owner; if (isFriend) { if (QualifierLoc) { @@ -2466,6 +2444,9 @@ if (!DC) return nullptr; } + CXXRecordDecl *Record = cast(DC); + Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + DeclarationNameInfo NameInfo = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs); @@ -2473,7 +2454,6 @@ adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo); // Build the instantiated method declaration. - CXXRecordDecl *Record = cast(DC); CXXMethodDecl *Method = nullptr; SourceLocation StartLoc = D->getInnerLocStart(); @@ -2783,13 +2763,11 @@ Inst->setImplicit(D->isImplicit()); if (auto *TC = D->getTypeConstraint()) { if (!D->isImplicit()) { - // Invented template parameter type constraints will be instantiated with - // the corresponding auto-typed parameter as it might reference other - // parameters. - - // TODO: Concepts: do not instantiate the constraint (delayed constraint - // substitution) - if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs)) + // Invented template parameter type constraints will be instantiated + // with the corresponding auto-typed parameter as it might reference + // other parameters. + if (SemaRef.SubstTypeConstraint(Inst, TC, TemplateArgs, + EvaluateConstraints)) return nullptr; } } @@ -4033,18 +4011,7 @@ if (Invalid) return nullptr; - // FIXME: Concepts: Substitution into requires clause should only happen when - // checking satisfaction. - Expr *InstRequiresClause = nullptr; - if (Expr *E = L->getRequiresClause()) { - EnterExpressionEvaluationContext ConstantEvaluated( - SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); - ExprResult Res = SemaRef.SubstExpr(E, TemplateArgs); - if (Res.isInvalid() || !Res.isUsable()) { - return nullptr; - } - InstRequiresClause = Res.get(); - } + Expr *InstRequiresClause = L->getRequiresClause(); TemplateParameterList *InstL = TemplateParameterList::Create(SemaRef.Context, L->getTemplateLoc(), @@ -4338,11 +4305,9 @@ ThisTypeQuals = Method->getMethodQualifiers(); } - TypeSourceInfo *NewTInfo - = SemaRef.SubstFunctionDeclType(OldTInfo, TemplateArgs, - D->getTypeSpecStartLoc(), - D->getDeclName(), - ThisContext, ThisTypeQuals); + TypeSourceInfo *NewTInfo = SemaRef.SubstFunctionDeclType( + OldTInfo, TemplateArgs, D->getTypeSpecStartLoc(), D->getDeclName(), + ThisContext, ThisTypeQuals, EvaluateConstraints); if (!NewTInfo) return nullptr; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13077,13 +13077,6 @@ NewCallOpType); } - // Transform the trailing requires clause - ExprResult NewTrailingRequiresClause; - if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) - // FIXME: Concepts: Substitution into requires clause should only happen - // when checking satisfaction. - NewTrailingRequiresClause = getDerived().TransformExpr(TRC); - // Create the local class that will describe the lambda. // FIXME: DependencyKind below is wrong when substituting inside a templated @@ -13118,7 +13111,7 @@ E->getCallOperator()->getEndLoc(), NewCallOpTSI->getTypeLoc().castAs().getParams(), E->getCallOperator()->getConstexprKind(), - NewTrailingRequiresClause.get()); + E->getCallOperator()->getTrailingRequiresClause()); LSI->CallOperator = NewCallOperator; diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -930,6 +930,7 @@ FD->setHasSkippedBody(Record.readInt()); FD->setIsMultiVersion(Record.readInt()); FD->setLateTemplateParsed(Record.readInt()); + FD->setFriendConstraintRefersToEnclosingTemplate(Record.readInt()); FD->setCachedLinkage(static_cast(Record.readInt())); FD->EndRangeLoc = readSourceLocation(); diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -802,7 +802,7 @@ E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); llvm::SmallVector Args; for (unsigned I = 0; I < NumTemplateArgs; ++I) - Args.push_back(Record.readTemplateArgument()); + Args.push_back(Record.readTemplateArgument(/*Canonicalize*/ true)); E->setTemplateArguments(Args); E->Satisfaction = E->isValueDependent() ? nullptr : ASTConstraintSatisfaction::Create(Record.getContext(), diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -563,6 +563,7 @@ Record.push_back(D->hasSkippedBody()); Record.push_back(D->isMultiVersion()); Record.push_back(D->isLateTemplateParsed()); + Record.push_back(D->FriendConstraintRefersToEnclosingTemplate()); Record.push_back(D->getLinkageInternal()); Record.AddSourceLocation(D->getEndLoc()); @@ -2285,6 +2286,7 @@ Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // SkippedBody Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // MultiVersion Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // LateParsed + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // FriendConstraintRefersToEnclosingTemplate Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // LocEnd Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // ODRHash diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp @@ -90,3 +90,24 @@ static_assert(C{}); // expected-note{{while checking constraint satisfaction for template 'C' required here}} static_assert(D{}); // expected-note{{while checking constraint satisfaction for template 'D' required here}} + +// Test the delayed instantiation, the 'foo' implementation shouldn't cause the +// constraint failure(or crash!) until the use to create 'y'. +namespace DelayedInst { +template +struct AAA { + template + requires(sizeof(T) == I) // expected-note {{because 'sizeof(int) == 5U' (4 == 5) evaluated to false}} + struct B { + static constexpr int a = 0; + }; + + static constexpr auto foo() { + return B::a; // expected-error{{constraints not satisfied for class template 'B' [with T = int]}} + } +}; + +constexpr auto x = AAA<4>::foo(); +constexpr auto y = AAA<5>::foo(); // expected-note {{in instantiation of member function 'DelayedInst::AAA<5>::foo' requested here}} + +} // namespace DelayedInst diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.order/class-template-partial-specializations.cpp @@ -52,6 +52,30 @@ static_assert(F::value == 3); static_assert(F::value == 1); +template +struct S { + template + struct F { + enum { value = 1 }; + }; + + template + requires C1 && C2 + struct F { + enum { value = 2 }; + }; + + template + requires C1 || C2 + struct F { + enum { value = 3 }; + }; +}; + +static_assert(S<1>::F::value == 2); +static_assert(S<1>::F::value == 3); +static_assert(S<1>::F::value == 1); + // Make sure atomic constraints subsume each other only if their parameter // mappings are identical. diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp --- a/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.order/var-template-partial-specializations.cpp @@ -51,5 +51,20 @@ static_assert(f == 3); static_assert(f == 1); - - +template +struct S { + template + static constexpr int f = 1; + + template + requires C1 && C2 + static constexpr int f = 2; + + template + requires C1 || C2 + static constexpr int f = 3; +}; + +static_assert(S<1>::f == 2); +static_assert(S<1>::f == 3); +static_assert(S<1>::f == 1); diff --git a/clang/test/Modules/concept_serialization.cpp b/clang/test/Modules/concept_serialization.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/concept_serialization.cpp @@ -0,0 +1,27 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -std=c++20 -fmodules-cache-path=%t -x c++ %s -verify +// expected-no-diagnostics +#pragma clang module build std +module std [system] { module concepts [system] {} } +#pragma clang module contents + +#pragma clang module begin std.concepts +template +T declval(); +template +concept common_reference_with = T::val; +template +concept input_or_output_iterator = true; +template +concept input_iterator = input_or_output_iterator && + common_reference_with)&&, T&>; +#pragma clang module end /*std.concepts*/ +#pragma clang module endbuild /*std*/ + +#pragma clang module import std.concepts +template +struct iter_value_or_void{}; +// ensure that we don't assert on a subsumption check due to improper +// deserialization. +template +struct iter_value_or_void{}; diff --git a/clang/test/SemaCXX/constrained-special-member-functions.cpp b/clang/test/SemaCXX/constrained-special-member-functions.cpp --- a/clang/test/SemaCXX/constrained-special-member-functions.cpp +++ b/clang/test/SemaCXX/constrained-special-member-functions.cpp @@ -196,17 +196,13 @@ // This is evaluated at the completion of CRTPBase, while `T` is not yet completed. // This is probably correct behavior. -// FIXME: We should not throw an error, instead SFINAE should make the constraint -// silently unsatisfied. See [temp.constr.constr]p5 template struct CRTPBase { - CRTPBase() requires (sizeof(T) > 0); // expected-error {{to an incomplete type}} + CRTPBase() requires (sizeof(T) > 0); CRTPBase() = default; }; struct Child : CRTPBase { int x; }; -// expected-note@-1 {{definition of 'Child' is not complete until}} -// expected-note@-2 {{in instantiation of template class 'CRTPBase' requested here}} static Child c; diff --git a/clang/test/SemaTemplate/concepts-friends.cpp b/clang/test/SemaTemplate/concepts-friends.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaTemplate/concepts-friends.cpp @@ -0,0 +1,396 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s + +template +concept constraint = false; +namespace temp_friend_9 { +// A non-template friend declaration with a requires-clause shall be a +// definition. ...Such a constrained friend function ... does not declare the +// same function or function template as a declaration in any other scope. +template +struct NonTemplateFriend { + friend void foo() + requires true + {} +}; + +// A friend function template with a constraint that depends on a template +// parameter from an enclosing template shall be a definition. Such a ... +// function template declaration does not declare the same function or +// function template as a declaration in any other scope. +template +struct TemplateFromEnclosing { + template + friend void foo() + requires constraint + {} + + T variable; + template + friend void foo2() + requires constraint + {} + + template + friend void foo3(T parmvar) + requires constraint + {} + + template + friend void foo4() + requires requires(T &req) { (void)req; } + {} + + using Alias = T; + template + friend void foo5() + requires constraint + {} + + // All of these refer to a parent, so these are not duplicate definitions. + struct ChildOfEnclosing { + template + friend void foo6() + requires constraint + {} + template + friend void foo7() + requires constraint + {} + template + friend void foo8(T parmvar) + requires constraint + {} + // This is NOT a duplicate since it itself is not a template. + friend void foo9() + requires true + {} + }; + template + struct TemplChildOfEnclosing { + template + friend void foo10() + requires constraint + {} + }; +}; + +// Doesn't meet either of the requirements in the above as they don't refer to +// an enclosing scope. +template +struct Redefinition { + template + friend void foo() // #REDEF + requires constraint + {} + + struct ChildOfRedef { + template + friend void foo2() // #REDEF2 + requires constraint + {} + }; + template + struct ChildOfRedef2 { + template + friend void foo3() // #REDEF3 + requires constraint + {} + }; +}; + +void bar() { + NonTemplateFriend S1; + NonTemplateFriend S2; + TemplateFromEnclosing S3; + TemplateFromEnclosing::ChildOfEnclosing S3b; + TemplateFromEnclosing S4; + TemplateFromEnclosing::ChildOfEnclosing S4b; + Redefinition S5; + Redefinition S6; + // expected-error@#REDEF {{redefinition of 'foo'}} + // expected-note@-2{{in instantiation of template class }} + // expected-note@#REDEF {{previous definition is here}} + Redefinition::ChildOfRedef S7; + Redefinition::ChildOfRedef S8; + // expected-error@#REDEF2 {{redefinition of 'foo2'}} + // expected-note@-2{{in instantiation of member class }} + // expected-note@#REDEF2 {{previous definition is here}} + + Redefinition::ChildOfRedef2 S9; + Redefinition::ChildOfRedef2 S10; + // expected-error@#REDEF3 {{redefinition of 'foo3'}} + // expected-note@-2{{in instantiation of template class }} + // expected-note@#REDEF3 {{previous definition is here}} +} +} // namespace temp_friend_9 + +namespace SameScopeRedefs { +template +struct NonTemplateFriend { + friend void foo() // #NTF1 + requires true + {} + friend void foo() // #NTF2 + requires true + {} +}; + +template +struct TemplateFromEnclosing { + template + friend void foo() // #TFE1 + requires constraint + {} + template + friend void foo() // #TFE2 + requires constraint + {} +}; +// Same as above, but doesn't require an instantiation pair to cause. +template +struct Redefinition { + template + friend void foo() // #RD1 + requires constraint + {} + template + friend void foo() // #RD2 + requires constraint + {} +}; +void bar() { + NonTemplateFriend S1; + // expected-error@#NTF2 {{redefinition of 'foo'}} + // expected-note@-2{{in instantiation of template class}} + // expected-note@#NTF1 {{previous definition is here}} + + TemplateFromEnclosing S2; + // expected-error@#TFE2 {{redefinition of 'foo'}} + // expected-note@-2{{in instantiation of template class}} + // expected-note@#TFE1 {{previous definition is here}} + + Redefinition S3; + // expected-error@#RD2 {{redefinition of 'foo'}} + // expected-note@-2{{in instantiation of template class}} + // expected-note@#RD1 {{previous definition is here}} +} +} // namespace SameScopeRedefs + +namespace LibCXXOperatorRedef { +template struct is_same { + static constexpr bool value = false; +}; +template struct is_same { + static constexpr bool value = false; +}; + +template +concept same_as = is_same::value; + +// An issue found from libcxx when trying to commit the deferred concepts patch. +// This caused an error of 'redefinition of funcN'. +template struct __range_adaptor_closure { + template + requires same_as<_Tp, _Closure> + friend constexpr decltype(auto) R1func1(_View &&__view, + _Closure &&__closure){}; + template + friend constexpr decltype(auto) R1func2(_View &&__view, + _Closure &&__closure) + requires same_as<_Tp, _Closure> + {}; + template _View, typename _Closure> + friend constexpr decltype(auto) R1func3(_View &&__view, + _Closure &&__closure){}; +}; + +struct A : __range_adaptor_closure {}; +struct B : __range_adaptor_closure {}; + +// These three fail because after the 1st pass of instantiation, they are still +// identical. +template struct __range_adaptor_closure2 { + template + requires same_as<_View, _Closure> + friend constexpr decltype(auto) R2func1(_View &&__view, // #FUNC1 + _Closure &&__closure){}; + template + friend constexpr decltype(auto) R2func2(_View &&__view, // #FUNC2 + _Closure &&__closure) + requires same_as<_View, _Closure> + {}; + template _Closure> + friend constexpr decltype(auto) R2func3(_View &&__view, // #FUNC3 + _Closure &&__closure){}; +}; + +struct A2 : __range_adaptor_closure2 {}; +struct B2 : __range_adaptor_closure2 {}; +// expected-error@#FUNC1{{redefinition of 'R2func1'}} +// expected-note@-2{{in instantiation of template class}} +// expected-note@#FUNC1{{previous definition is here}} +// expected-error@#FUNC2{{redefinition of 'R2func2'}} +// expected-note@#FUNC2{{previous definition is here}} +// expected-error@#FUNC3{{redefinition of 'R2func3'}} +// expected-note@#FUNC3{{previous definition is here}} + +// These three are fine, they all depend on the parent template parameter, so +// are different despite ::type not being valid. +template struct __range_adaptor_closure3 { + template + requires same_as + friend constexpr decltype(auto) R3func1(_View &&__view, + _Closure &&__closure){}; + template + friend constexpr decltype(auto) R3func2(_View &&__view, + _Closure &&__closure) + requires same_as + {}; + template _View, typename _Closure> + friend constexpr decltype(auto) R3func3(_View &&__view, + _Closure &&__closure){}; +}; + +struct A3 : __range_adaptor_closure3 {}; +struct B3 : __range_adaptor_closure3 {}; + +template struct __range_adaptor_closure4 { + template + requires same_as<_Tp, _View> + // expected-note@+1{{previous definition is here}} + void foo1(_View &&, _Closure &&) {} + template + requires same_as<_Tp, _View> + // expected-error@+1{{class member cannot be redeclared}} + void foo1(_View &&, _Closure &&) {} + + template + // expected-note@+1{{previous definition is here}} + void foo2(_View &&, _Closure &&) + requires same_as<_Tp, _View> + {} + template + // expected-error@+1{{class member cannot be redeclared}} + void foo2(_View &&, _Closure &&) + requires same_as<_Tp, _View> + {} + + template _View, typename _Closure> + // expected-note@+1{{previous definition is here}} + void foo3(_View &&, _Closure &&) {} + template _View, typename _Closure> + // expected-error@+1{{class member cannot be redeclared}} + void foo3(_View &&, _Closure &&) {} +}; + +// Requires instantiation to fail, so no errors here. +template struct __range_adaptor_closure5 { + template U> + friend void foo() {} + template U> + friend void foo() {} +}; + +template struct __range_adaptor_closure6 { + template U> + friend void foo() {} // #RAC6FOO1 + template U> + friend void foo() {} // #RAC6FOO2 +}; +struct A6 : __range_adaptor_closure6 {}; +// expected-error@#RAC6FOO2{{redefinition of 'foo'}} +// expected-note@-2{{in instantiation of template class}} +// expected-note@#RAC6FOO1{{previous definition is here}} + +template struct S1 { + template + friend void dupe() {} // #S1DUPE + + template + requires same_as + friend void dupe2() {} // #S1DUPE2 +}; +template struct S2 { + template + friend void dupe() {} // #S2DUPE + + template + requires same_as + friend void dupe2() {} // #S2DUPE2 +}; + +template struct S3 { + template + requires same_as + friend void dupe() {} +}; +template struct S4 { + template + requires same_as + friend void dupe() {} +}; + +// Same as S3 and S4, but aren't instantiated with the same T. +template struct S5 { + template + requires same_as + friend void not_dupe() {} +}; +template struct S6 { + template + requires same_as + friend void not_dupe() {} +}; + +template struct S7 { + void not_dupe() + requires same_as + {} +}; + +void useS() { + S1 s1; + S2 s2; + // expected-error@#S2DUPE{{redefinition}} + // expected-note@-2{{in instantiation of template class}} + // expected-note@#S1DUPE{{previous definition is here}} + // expected-error@#S2DUPE2{{redefinition}} + // expected-note@#S1DUPE2{{previous definition is here}} + + // OK, they have different 'scopes'. + S3 s3; + S4 s4; + + // OK, because only instantiated with different T. + S5 s5; + S6 s6; + + S7 s7; +} + +} // namespace LibCXXOperatorRedef + +namespace NamedDeclRefs { + namespace my_std { + template + concept Outer = true; + template + using Inner = T; + } + template + struct Proxy { + template + friend constexpr void RefOuter() + requires my_std::Outer, my_std::Inner>{} + template + friend constexpr void NoRefOuter() // #NOREFOUTER + requires my_std::Outer, my_std::Inner>{} + }; + void use() { + Proxy p; + Proxy p2; + // expected-error@#NOREFOUTER {{redefinition of 'NoRefOuter'}} + // expected-note@-2{{in instantiation of template class}} + // expected-note@#NOREFOUTER{{previous definition is here}} + } +} // namespace NamedDeclRefs diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -262,3 +262,449 @@ template struct S {}; void f(C auto); } // namespace GH55567 + +namespace SubConstraintChecks { +template +concept TrueConstraint = true; +template +concept FalseConstraint = false; + +template +class ContainsConstrainedFuncTrue { +public: + template + static void func(V &&, Constrained &&C); +}; +template +class ContainsConstrainedFuncFalse { +public: + template + static void func(V &&, Constrained &&C); +}; + +template +concept TrueConstraint2 = + requires(float &&t) { + ContainsConstrainedFuncTrue::func(5, 0.0); + }; +template +concept FalseConstraint2 = + requires(float &&t) { + ContainsConstrainedFuncFalse::func(5, 0.0); // #FC2_CONSTR + }; + +template +void useTrue(int F) + requires TrueConstraint2 +{} + +template +void useFalse(int F) // #USE_FALSE + requires FalseConstraint2 // #USE_FALSE_CONSTR +{} + +// Should only diagnose 'false' once instantiated. +void UseUse() { + useTrue(5); + useFalse(5); + // expected-error@-1{{no matching function for call to 'useFalse'}} + // expected-note@#USE_FALSE{{constraints not satisfied}} + // expected-note@#USE_FALSE_CONSTR{{because 'int' does not satisfy 'FalseConstraint2'}} + // expected-note@#FC2_CONSTR {{would be invalid: no matching function for call to 'func'}} +} +} // namespace SubConstraintChecks + +namespace DeducedTemplateArgs { +template struct ItrTraits { + template struct Ptr { + }; + template + requires requires { typename PtrItr::pointer; } + struct Ptr { + using type = typename Itr::pointer; + }; + using pointer = typename Ptr::type; // #TRAITS_PTR +}; + +struct complete_itr { + using pointer = int; +}; + +template class Complete { + using ItrType = ItrTraits; + ItrType begin() noexcept { return ItrType(); } +}; + +// This version doesn't have 'pointer', so error confirms we are in the first +// verison of 'Ptr'. +struct not_complete_itr { +}; + +template class NotComplete { + using ItrType = ItrTraits; + ItrType begin() noexcept { return ItrType(); } + // expected-error@#TRAITS_PTR{{no type named 'type' in }} + // expected-note@-2{{in instantiation of template class }} +}; +} // namespace DeducedTemplateArgs + +namespace DeferredInstantiationInstScope { +template +struct remove_ref { + using type = T; +}; +template +struct remove_ref { + using type = T; +}; +template +struct remove_ref { + using type = T; +}; + +template +constexpr bool IsInt = PR54443::is_same::type, + int>::value; + +template +void SingleDepthReferencesTop(U &&u) { + struct lc { + void operator()() // #SDRT_OP + requires IsInt // #SDRT_REQ + {} + }; + lc lv; + lv(); // #SDRT_CALL +} + +template +void SingleDepthReferencesTopNotCalled(U &&u) { + struct lc { + void operator()() + requires IsInt + {} + }; + lc lv; +} + +template +void SingleDepthReferencesTopCalled(U &&u) { + struct lc { + void operator()() // #CALLOP + requires IsInt // #CONSTR + {} + }; + lc lv; + lv(); + // expected-error@-1{{no matching function for call to object of type 'lc'}} + // expected-note@#SDRTC{{in instantiation of function template}} + // expected-note@#CALLOP{{constraints not satisfied}} + // expected-note@#CONSTR{{substituted constraint expression is ill-formed}} +} + +template +void SingleDepthReferencesTopLambda(U &&u) { + []() + requires IsInt + {}(); +} + +template +void DoubleDepthReferencesTop(U &&u) { + struct lc { // #DDRT_STRCT + void operator()() { + struct lc2 { + void operator()() // #DDRT_OP + requires IsInt // #DDRT_REQ + {} + }; + lc2 lv2; + lv2(); // #DDRT_CALL + } + }; + lc lv; + lv(); +} + +template +void DoubleDepthReferencesTopLambda(U &&u) { + []() { []() + requires IsInt + {}(); }(); +} + +template +void DoubleDepthReferencesAll(U &&u) { + struct lc { // #DDRA_STRCT + void operator()(U &&u2) { + struct lc2 { + void operator()(U &&u3) // #DDRA_OP + requires IsInt && // #DDRA_REQ + IsInt && IsInt + {} + }; + lc2 lv2; + lv2(u2); // #DDRA_CALL + } + }; + lc lv; + lv(u); +} + +template +void DoubleDepthReferencesAllLambda(U &&u) { + [](U &&u2) { + [](U && u3) + requires IsInt && + IsInt && IsInt + {}(u2); + }(u); +} + +template +struct CausesFriendConstraint { + template + friend void FriendFunc(CausesFriendConstraint, V) // #FF_DECL + requires IsInt && + IsInt // #FF_REQ + {} +}; +// FIXME: Re-enable this test when constraints are allowed to refer to captures. +// template +// void ChecksCapture(T x) { +// [y = x]() requires(IsInt){}(); +// } + +template +void ChecksLocalVar(T x) { + T Local; + []() + requires(IsInt) + {}(); +} + +template +void LocalStructMemberVar(T x) { + struct S { + T local; + void foo() + requires(IsInt) // #LSMV_REQ + {} + } s; + s.foo(); // #LSMV_CALL +}; + +template +struct ChecksMemberVar { + T t; + void foo() + requires(IsInt) // #CMV_FOO + {} + template + void foo2() // #CMV_FOO2 + requires(IsInt) // #CMV_FOO2_REQ + {} +}; + +void test_dependent() { + int v = 0; + float will_fail; + SingleDepthReferencesTop(v); + SingleDepthReferencesTop(will_fail); + // expected-error@#SDRT_CALL{{no matching function for call to object of type 'lc'}} + // expected-note@-2{{in instantiation of function template specialization}} + // expected-note@#SDRT_OP{{candidate function not viable}} + // expected-note@#SDRT_REQ{{'IsInt' evaluated to false}} + + SingleDepthReferencesTopNotCalled(v); + // Won't error unless we try to call it. + SingleDepthReferencesTopNotCalled(will_fail); + SingleDepthReferencesTopCalled(v); // #SDRTC + SingleDepthReferencesTopLambda(v); + // FIXME: This should error on constraint failure! (Lambda!) + SingleDepthReferencesTopLambda(will_fail); + DoubleDepthReferencesTop(v); + DoubleDepthReferencesTop(will_fail); + // expected-error@#DDRT_CALL{{no matching function for call to object of type 'lc2'}} + // expected-note@-2{{in instantiation of function template specialization}} + // expected-note@#DDRT_STRCT{{in instantiation of member function}} + // expected-note@#DDRT_OP{{candidate function not viable}} + // expected-note@#DDRT_REQ{{'IsInt' evaluated to false}} + + DoubleDepthReferencesTopLambda(v); + // FIXME: This should error on constraint failure! (Lambda!) + DoubleDepthReferencesTopLambda(will_fail); + DoubleDepthReferencesAll(v); + DoubleDepthReferencesAll(will_fail); + // expected-error@#DDRA_CALL{{no matching function for call to object of type 'lc2'}} + // expected-note@-2{{in instantiation of function template specialization}} + // expected-note@#DDRA_STRCT{{in instantiation of member function}} + // expected-note@#DDRA_OP{{candidate function not viable}} + // expected-note@#DDRA_REQ{{'IsInt' evaluated to false}} + + DoubleDepthReferencesAllLambda(v); + // FIXME: This should error on constraint failure! (Lambda!) + DoubleDepthReferencesAllLambda(will_fail); + + CausesFriendConstraint CFC; + FriendFunc(CFC, 1); + FriendFunc(CFC, 1.0); + // expected-error@-1{{no matching function for call to 'FriendFunc'}} + // expected-note@#FF_DECL{{constraints not satisfied}} + // expected-note@#FF_REQ{{because 'IsInt' evaluated to false}} + + // FIXME: Re-enable this test when constraints are allowed to refer to captures. + // ChecksCapture(v); + + ChecksLocalVar(v); + // FIXME: This should error on constraint failure! (Lambda!) + ChecksLocalVar(will_fail); + + LocalStructMemberVar(v); + LocalStructMemberVar(will_fail); + // expected-error@#LSMV_CALL{{invalid reference to function 'foo'}} + // expected-note@-2{{in instantiation of function template specialization}} + // expected-note@#LSMV_REQ{{because 'IsIntlocal)>' evaluated to false}} + + ChecksMemberVar CMV; + CMV.foo(); + CMV.foo2(); + + ChecksMemberVar CMV2; + CMV2.foo(); + // expected-error@-1{{invalid reference to function 'foo'}} + // expected-note@#CMV_FOO{{because 'IsIntt)>' evaluated to false}} + CMV2.foo2(); + // expected-error@-1{{no matching member function for call to 'foo2'}} + // expected-note@#CMV_FOO2{{constraints not satisfied}} + // expected-note@#CMV_FOO2_REQ{{because 'IsIntt)>' evaluated to false}} +} +} // namespace DeferredInstantiationInstScope + +// Ane example of evaluating a concept at two different depths in the same +// evaluation. No diagnostic is expected. +namespace SameConceptDifferentDepth { +template +concept sentinel_for = + requires(_Ip __i) { + __i++; + }; + +template +concept bidirectional_iterator = + sentinel_for<_Ip>; + +template +class move_iterator { +public: + auto operator++(int) + requires sentinel_for<_Iter>{} +}; + +static_assert(bidirectional_iterator>); +} // namespace SameConceptDifferentDepth + +namespace VarInit { +template +concept __can_reference = true; + +template +class common_iterator { +public: + common_iterator() { + constexpr auto x = requires(_Iter & __i) { { __i } -> __can_reference; }; + } +}; + +void test() { + auto commonIter1 = common_iterator(); +} +} // namespace VarInit + + +namespace InlineFriendOperator { +template +concept C = true; +template +class counted_iterator { + _Iter I; +public: + constexpr counted_iterator() = default; + friend constexpr auto operator+( // expected-note {{candidate function not viable}} + int __n, const counted_iterator &__x) + requires C + { + return __x + __n; // expected-error{{invalid operands to binary expression}} + } +}; + +constexpr bool test() { + counted_iterator iter; + auto x = 2 + iter; // expected-note{{in instantiation of member function 'InlineFriendOperator::operator+'}} + + return true; +} +} // namespace InlineFriendOperator + +namespace ClassTemplateInstantiation { +struct Type; +template < typename A, typename B, typename C> + concept ConstraintF = false; // #ConstraintF +template < typename A, typename B, typename C> + concept ConstraintT = true; + +template < typename T > struct Parent { + template < typename U, ConstraintT > struct ChildT{}; + ChildT CauseInstT; + template < typename U, ConstraintF > struct ChildF{};// #ChildF + ChildF CauseInstF; //#CauseInstF +}; + +// expected-error@#CauseInstF{{constraints not satisfied for class template}} +// expected-note@+3{{in instantiation of template class}} +// expected-note@#ChildF{{evaluated to false}} +// expected-note@#ConstraintF{{because 'false' evaluated to false}} +Parent Inst; +} // namespace ClassTemplateInstantiation + +namespace SelfFriend { + template + concept Constraint = requires (T i) { (*i); }; + template + concept Constraint2 = requires (T i) { (*i); }; + + template + struct Iterator { + template + friend class Iterator; + void operator*(); + }; + + template // #ITER_BAD + struct IteratorBad { + template //#ITER_BAD_FRIEND + friend class IteratorBad; + void operator*(); + }; + + Iterator I; + Iterator I2; + IteratorBad I3; // expected-error@#ITER_BAD_FRIEND{{constraint differs}} + // expected-note@-1{{in instantiation of template class}} + // expected-note@#ITER_BAD{{previous template declaration}} +} // namespace SelfFriend + + +namespace ConstrainedMemberVarTemplate { +template struct Container { + static constexpr long arity = Size; + template + requires(sizeof(U) == arity) // #CMVT_REQ + using var_templ = int; +}; +Container<4>::var_templ inst; +Container<5>::var_templ inst_fail; +// expected-error@-1{{constraints not satisfied for alias template 'var_templ'}} +// expected-note@#CMVT_REQ{{because 'sizeof(int) == arity' (4 == 5) evaluated to false}} +} // namespace ConstrainedMemberVarTemplate + diff --git a/clang/test/SemaTemplate/deferred-concept-inst.cpp b/clang/test/SemaTemplate/deferred-concept-inst.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaTemplate/deferred-concept-inst.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify -fsyntax-only -Wno-unused-value +// expected-no-diagnostics + +namespace GithubBug44178 { +template +struct CRTP { + void call_foo() + requires requires(D &v) { v.foo(); } + { + static_cast(this)->foo(); + } +}; + +struct Test : public CRTP { + void foo() {} +}; + +int main() { + Test t; + t.call_foo(); + return 0; +} +} // namespace GithubBug44178 diff --git a/clang/test/SemaTemplate/instantiate-requires-clause.cpp b/clang/test/SemaTemplate/instantiate-requires-clause.cpp --- a/clang/test/SemaTemplate/instantiate-requires-clause.cpp +++ b/clang/test/SemaTemplate/instantiate-requires-clause.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify +// RUN: %clang_cc1 -std=c++2a -x c++ %s -Wno-unused-value -verify template requires ((sizeof(Args) == 1), ...) // expected-note@-1 {{because '(sizeof(int) == 1) , (sizeof(char) == 1) , (sizeof(int) == 1)' evaluated to false}} @@ -40,6 +40,20 @@ static_assert(S::f(1)); +// Similar to the 'S' test, but tries to use 'U' in the requires clause. +template +struct S1 { + // expected-note@+3 {{candidate template ignored: constraints not satisfied [with U = int]}} + // expected-note@+3 {{because substituted constraint expression is ill-formed: type 'int' cannot be used prior to '::' because it has no members}} + template + static constexpr auto f(U const index) + requires(U::foo) + { return true; } +}; + +// expected-error@+1 {{no matching function for call to 'f'}} +static_assert(S1::f(1)); + constexpr auto value = 0; template diff --git a/clang/test/SemaTemplate/trailing-return-short-circuit.cpp b/clang/test/SemaTemplate/trailing-return-short-circuit.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaTemplate/trailing-return-short-circuit.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s + +template + requires(sizeof(T) > 2) || T::value // #FOO_REQ +void Foo(T){}; // #FOO + +template +void TrailingReturn(T) // #TRAILING + requires(sizeof(T) > 2) || // #TRAILING_REQ + T::value // #TRAILING_REQ_VAL +{}; +template +struct HasValue { + static constexpr bool value = B; +}; +static_assert(sizeof(HasValue) <= 2); + +template +struct HasValueLarge { + static constexpr bool value = B; + int I; +}; +static_assert(sizeof(HasValueLarge) > 2); + +void usage() { + // Passes the 1st check, short-circuit so the 2nd ::value is not evaluated. + Foo(1.0); + TrailingReturn(1.0); + + // Fails the 1st check, but has a ::value, so the check happens correctly. + Foo(HasValue{}); + TrailingReturn(HasValue{}); + + // Passes the 1st check, but would have passed the 2nd one. + Foo(HasValueLarge{}); + TrailingReturn(HasValueLarge{}); + + // Fails the 1st check, fails 2nd because there is no ::value. + Foo(true); + // expected-error@-1{{no matching function for call to 'Foo'}} + // expected-note@#FOO{{candidate template ignored: constraints not satisfied [with T = bool]}} + // expected-note@#FOO_REQ{{because 'sizeof(_Bool) > 2' (1 > 2) evaluated to false}} + // expected-note@#FOO_REQ{{because substituted constraint expression is ill-formed: type 'bool' cannot be used prior to '::' because it has no members}} + + TrailingReturn(true); + // expected-error@-1{{no matching function for call to 'TrailingReturn'}} + // expected-note@#TRAILING{{candidate template ignored: constraints not satisfied [with T = bool]}} + // expected-note@#TRAILING_REQ{{because 'sizeof(_Bool) > 2' (1 > 2) evaluated to false}} + // expected-note@#TRAILING_REQ_VAL{{because substituted constraint expression is ill-formed: type 'bool' cannot be used prior to '::' because it has no members}} + + // Fails the 1st check, fails 2nd because ::value is false. + Foo(HasValue{}); + // expected-error@-1 {{no matching function for call to 'Foo'}} + // expected-note@#FOO{{candidate template ignored: constraints not satisfied [with T = HasValue]}} + // expected-note@#FOO_REQ{{because 'sizeof(HasValue) > 2' (1 > 2) evaluated to false}} + // expected-note@#FOO_REQ{{and 'HasValue::value' evaluated to false}} + TrailingReturn(HasValue{}); + // expected-error@-1 {{no matching function for call to 'TrailingReturn'}} + // expected-note@#TRAILING{{candidate template ignored: constraints not satisfied [with T = HasValue]}} + // expected-note@#TRAILING_REQ{{because 'sizeof(HasValue) > 2' (1 > 2) evaluated to false}} + // expected-note@#TRAILING_REQ_VAL{{and 'HasValue::value' evaluated to false}} +}