Author: Erich Keane Date: 2022-05-09T06:29:47-07:00 New Revision: a425cac31e2e4cee8e14b7b9a99c8ba17c1ebb52
URL: https://github.com/llvm/llvm-project/commit/a425cac31e2e4cee8e14b7b9a99c8ba17c1ebb52 DIFF: https://github.com/llvm/llvm-project/commit/a425cac31e2e4cee8e14b7b9a99c8ba17c1ebb52.diff LOG: "Re-apply 4b6c2cd642 "Deferred Concept Instantiation Implementation"""" This includes a fix for the libc++ issue I ran across with friend declarations not properly being identified as overloads. This reverts commit 45c07db31cc76802a1a2e41bed1ce9c1b8198181. Added: clang/test/SemaTemplate/deferred-concept-inst.cpp clang/test/SemaTemplate/trailing-return-short-circuit.cpp Modified: clang/docs/ReleaseNotes.rst clang/include/clang/AST/Decl.h clang/include/clang/AST/DeclBase.h clang/include/clang/Sema/Sema.h clang/include/clang/Sema/Template.h clang/lib/AST/ASTImporter.cpp clang/lib/AST/Decl.cpp clang/lib/AST/DeclBase.cpp clang/lib/ExtractAPI/ExtractAPIConsumer.cpp clang/lib/Sema/SemaConcept.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Sema/TreeTransform.h clang/lib/Serialization/ASTReaderDecl.cpp clang/lib/Serialization/ASTWriterDecl.cpp clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp clang/test/SemaTemplate/concepts.cpp clang/test/SemaTemplate/instantiate-requires-clause.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 7cdd77b2df3a4..73fb5a4f3c1f5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -323,10 +323,11 @@ C++20 Feature Support - No longer attempt to evaluate a consteval UDL function call at runtime when it is called through a template instantiation. This fixes `Issue 54578 <https://github.com/llvm/llvm-project/issues/54578>`_. - -- Implemented ``__builtin_source_location()``, which enables library support - for ``std::source_location``. - +- Implemented `__builtin_source_location()` which enables library support for std::source_location. +- 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 <https://github.com/llvm/llvm-project/issues/44178>`_. - The mangling scheme for C++20 modules has incompatibly changed. The initial mangling was discovered not to be reversible, and the weak ownership design decision did not give the backwards compatibility diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index c2133f4e79a15..cc84e746d8d84 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1890,7 +1890,9 @@ class FunctionDecl : public DeclaratorDecl, TK_FunctionTemplateSpecialization, // A function template specialization that hasn't yet been resolved to a // particular specialized function template. - TK_DependentFunctionTemplateSpecialization + TK_DependentFunctionTemplateSpecialization, + // A non templated function which is in a dependent scope. + TK_DependentNonTemplate }; /// Stashed information about a defaulted function definition whose body has @@ -1939,20 +1941,21 @@ class FunctionDecl : public DeclaratorDecl, /// The template or declaration that this declaration /// describes or was instantiated from, respectively. /// - /// For non-templates, this value will be NULL. For function - /// declarations that describe a function template, this will be a - /// pointer to a FunctionTemplateDecl. For member functions - /// of class template specializations, this will be a MemberSpecializationInfo - /// pointer containing information about the specialization. - /// For function template specializations, this will be a - /// FunctionTemplateSpecializationInfo, which contains information about - /// the template being specialized and the template arguments involved in - /// that specialization. - llvm::PointerUnion<FunctionTemplateDecl *, - MemberSpecializationInfo *, + /// For non-templates this value will be NULL, unless this non-template + /// function declaration was declared directly inside of a function template, + /// in which case this will have a pointer to a FunctionDecl, stored in the + /// NamedDecl. For function declarations that describe a function template, + /// this will be a pointer to a FunctionTemplateDecl, stored in the NamedDecl. + /// For member functions of class template specializations, this will be a + /// MemberSpecializationInfo pointer containing information about the + /// specialization. For function template specializations, this will be a + /// FunctionTemplateSpecializationInfo, which contains information about the + /// template being specialized and the template arguments involved in that + /// specialization. + llvm::PointerUnion<NamedDecl *, MemberSpecializationInfo *, FunctionTemplateSpecializationInfo *, DependentFunctionTemplateSpecializationInfo *> - TemplateOrSpecialization; + TemplateOrSpecialization; /// Provides source/type location info for the declaration name embedded in /// the DeclaratorDecl base class. @@ -2688,6 +2691,11 @@ class FunctionDecl : public DeclaratorDecl, setInstantiationOfMemberFunction(getASTContext(), FD, TSK); } + /// Specify that this function declaration was instantiated from FunctionDecl + /// FD. This is only used if this is a function declaration declared locally + /// inside of a function template. + void setInstantiatedFromDecl(FunctionDecl *FD); + /// Retrieves the function template that is described by this /// function declaration. /// @@ -2702,6 +2710,8 @@ class FunctionDecl : public DeclaratorDecl, /// FunctionTemplateDecl from a FunctionDecl. FunctionTemplateDecl *getDescribedFunctionTemplate() const; + FunctionDecl *getInstantiatedFromDecl() const; + void setDescribedFunctionTemplate(FunctionTemplateDecl *Template); /// Determine whether this function is a function template diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index a4a44e0b30e64..54cecade3a709 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -906,6 +906,14 @@ class alignas(8) Decl { const_cast<const Decl*>(this)->getParentFunctionOrMethod()); } + /// Does the same thing as getParentFunctionOrMethod, except starts with the + /// lexical declaration context instead. + const DeclContext *getLexicalParentFunctionOrMethod() const; + DeclContext *getLexicalParentFunctionOrMethod() { + return const_cast<DeclContext *>( + const_cast<const Decl *>(this)->getLexicalParentFunctionOrMethod()); + } + /// Retrieves the "canonical" declaration of the given declaration. virtual Decl *getCanonicalDecl() { return this; } const Decl *getCanonicalDecl() const { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 27603f0b891f3..7bcf8c662d273 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6989,7 +6989,34 @@ class Sema final { 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<ArrayRef<TemplateArgument>> 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<MultiLevelTemplateArgumentList> + SetupConstraintCheckingTemplateArgumentsAndScope( + FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs, + LocalInstantiationScope &Scope); + + // Keep track of whether we are evaluating a constraint. + unsigned ConstraintEvaluationDepth = 0; + + class ConstraintEvalRAII { + Sema &S; + + public: + ConstraintEvalRAII(Sema &S) : S(S) { ++S.ConstraintEvaluationDepth; } + ~ConstraintEvalRAII() { --S.ConstraintEvaluationDepth; } + }; + public: + bool IsEvaluatingAConstraint() { return ConstraintEvaluationDepth > 0; } const NormalizedConstraint * getNormalizedAssociatedConstraints( NamedDecl *ConstrainedDecl, ArrayRef<const Expr *> AssociatedConstraints); @@ -7019,8 +7046,10 @@ class Sema final { /// check (either a concept or a constrained entity). /// \param ConstraintExprs a list of constraint expressions, treated as if /// they were 'AND'ed together. - /// \param TemplateArgs the list of template arguments to substitute into the - /// constraint expression. + /// \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 @@ -7030,7 +7059,40 @@ class Sema final { /// false otherwise. bool CheckConstraintSatisfaction( const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, - ArrayRef<TemplateArgument> TemplateArgs, + const MultiLevelTemplateArgumentList &TemplateArgList, + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { + llvm::SmallVector<Expr *, 4> Converted; + return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted, + TemplateArgList, 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<const Expr *> ConstraintExprs, + llvm::SmallVectorImpl<Expr *> &ConvertedConstraints, + const MultiLevelTemplateArgumentList &TemplateArgList, SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); /// \brief Check whether the given non-dependent constraint expression is @@ -7066,9 +7128,9 @@ class Sema final { /// /// \returns true if the constrains are not satisfied or could not be checked /// for satisfaction, false if the constraints are satisfied. - bool EnsureTemplateArgumentListConstraints(TemplateDecl *Template, - ArrayRef<TemplateArgument> TemplateArgs, - SourceRange TemplateIDRange); + bool EnsureTemplateArgumentListConstraints( + TemplateDecl *Template, MultiLevelTemplateArgumentList TemplateArgs, + SourceRange TemplateIDRange); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. @@ -8802,7 +8864,8 @@ class Sema final { 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 @@ -9535,6 +9598,11 @@ class Sema final { ExtParameterInfoBuilder &ParamInfos); ExprResult SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); + // 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. diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h index 540d2c9aa87e7..c8179dd9d15b6 100644 --- a/clang/include/clang/Sema/Template.h +++ b/clang/include/clang/Sema/Template.h @@ -75,6 +75,9 @@ enum class TemplateSubstitutionKind : char { class MultiLevelTemplateArgumentList { /// The template argument list at a certain template depth using ArgList = ArrayRef<TemplateArgument>; + using ArgListsIterator = SmallVector<ArgList, 4>::reverse_iterator; + using ConstArgListsIterator = + SmallVector<ArgList, 4>::const_reverse_iterator; /// The template argument lists, stored from the innermost template /// argument list (first) to the outermost template argument list (last). @@ -121,6 +124,12 @@ enum class TemplateSubstitutionKind : char { return TemplateArgumentLists.size(); } + /// Determine the number of substituted args at 'Depth'. + unsigned getNumSubstitutedArgs(unsigned Depth) const { + assert(NumRetainedOuterLevels <= Depth && Depth < getNumLevels()); + return TemplateArgumentLists[getNumLevels() - Depth - 1].size(); + } + unsigned getNumRetainedOuterLevels() const { return NumRetainedOuterLevels; } @@ -158,6 +167,14 @@ enum class TemplateSubstitutionKind : char { return !(*this)(Depth, Index).isNull(); } + bool isAnyArgInstantiationDependent() const { + for (ArgList List : TemplateArgumentLists) + for (const TemplateArgument &TA : List) + if (TA.isInstantiationDependent()) + return true; + return false; + } + /// Clear out a specific template argument. void setArgument(unsigned Depth, unsigned Index, TemplateArgument Arg) { @@ -197,6 +214,16 @@ enum class TemplateSubstitutionKind : char { const ArgList &getInnermost() const { return TemplateArgumentLists.front(); } + + /// Retrieve the outermost template argument list. + const ArgList &getOutermost() const { return TemplateArgumentLists.back(); } + + ArgListsIterator begin() { return TemplateArgumentLists.rbegin(); } + ConstArgListsIterator begin() const { + return TemplateArgumentLists.rbegin(); + } + ArgListsIterator end() { return TemplateArgumentLists.rend(); } + ConstArgListsIterator end() const { return TemplateArgumentLists.rend(); } }; /// The context in which partial ordering of function templates occurs. diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index e2294088908c5..be77d68ca7a56 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3109,6 +3109,11 @@ Error ASTNodeImporter::ImportTemplateInformation( case FunctionDecl::TK_FunctionTemplate: return Error::success(); + case FunctionDecl::TK_DependentNonTemplate: + if (Expected<FunctionDecl *> InstFDOrErr = + import(FromFD->getInstantiatedFromDecl())) + ToFD->setInstantiatedFromDecl(*InstFDOrErr); + return Error::success(); case FunctionDecl::TK_MemberSpecialization: { TemplateSpecializationKind TSK = FromFD->getTemplateSpecializationKind(); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 911479bee5ff9..e6b949a6f06df 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3729,8 +3729,12 @@ const IdentifierInfo *FunctionDecl::getLiteralIdentifier() const { FunctionDecl::TemplatedKind FunctionDecl::getTemplatedKind() const { if (TemplateOrSpecialization.isNull()) return TK_NonTemplate; - if (TemplateOrSpecialization.is<FunctionTemplateDecl *>()) + if (auto *ND = TemplateOrSpecialization.dyn_cast<NamedDecl *>()) { + if (isa<FunctionDecl>(ND)) + return TK_DependentNonTemplate; + assert(isa<FunctionTemplateDecl>(ND) && "No other types it could be?"); return TK_FunctionTemplate; + } if (TemplateOrSpecialization.is<MemberSpecializationInfo *>()) return TK_MemberSpecialization; if (TemplateOrSpecialization.is<FunctionTemplateSpecializationInfo *>()) @@ -3771,13 +3775,26 @@ FunctionDecl::setInstantiationOfMemberFunction(ASTContext &C, } FunctionTemplateDecl *FunctionDecl::getDescribedFunctionTemplate() const { - return TemplateOrSpecialization.dyn_cast<FunctionTemplateDecl *>(); + return dyn_cast_or_null<FunctionTemplateDecl>( + TemplateOrSpecialization.dyn_cast<NamedDecl *>()); } -void FunctionDecl::setDescribedFunctionTemplate(FunctionTemplateDecl *Template) { +void FunctionDecl::setDescribedFunctionTemplate( + FunctionTemplateDecl *Template) { assert(TemplateOrSpecialization.isNull() && "Member function is already a specialization"); - TemplateOrSpecialization = Template; + TemplateOrSpecialization = static_cast<NamedDecl *>(Template); +} + +void FunctionDecl::setInstantiatedFromDecl(FunctionDecl *FD) { + assert(TemplateOrSpecialization.isNull() && + "function is already a specialization"); + TemplateOrSpecialization = static_cast<NamedDecl *>(FD); +} + +FunctionDecl *FunctionDecl::getInstantiatedFromDecl() const { + return dyn_cast_or_null<FunctionDecl>( + TemplateOrSpecialization.dyn_cast<NamedDecl *>()); } bool FunctionDecl::isImplicitlyInstantiable() const { diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index d5ad636810544..d666d6b10a2e3 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -293,6 +293,16 @@ const DeclContext *Decl::getParentFunctionOrMethod() const { return nullptr; } +const DeclContext *Decl::getLexicalParentFunctionOrMethod() const { + for (const DeclContext *DC = getLexicalDeclContext(); + DC && !DC->isTranslationUnit() && !DC->isNamespace(); + DC = DC->getParent()) + if (DC->isFunctionOrMethod()) + return DC; + + return nullptr; +} + //===----------------------------------------------------------------------===// // PrettyStackTraceDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp index a7b0a1ac98a78..4975111356393 100644 --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -303,6 +303,7 @@ class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> { // Skip templated functions. switch (Decl->getTemplatedKind()) { case FunctionDecl::TK_NonTemplate: + case FunctionDecl::TK_DependentNonTemplate: break; case FunctionDecl::TK_MemberSpecialization: case FunctionDecl::TK_FunctionTemplateSpecialization: diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index c78b0df6ff488..546fc71c2893a 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -30,6 +30,7 @@ using namespace sema; namespace { class LogicalBinOp { + SourceLocation Loc; OverloadedOperatorKind Op = OO_None; const Expr *LHS = nullptr; const Expr *RHS = nullptr; @@ -40,12 +41,14 @@ class LogicalBinOp { Op = BinaryOperator::getOverloadedOperator(BO->getOpcode()); LHS = BO->getLHS(); RHS = BO->getRHS(); + Loc = BO->getExprLoc(); } else if (auto *OO = dyn_cast<CXXOperatorCallExpr>(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 +59,25 @@ class LogicalBinOp { const Expr *getLHS() const { return LHS; } const Expr *getRHS() const { return RHS; } + + ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS) { + return recreateBinOp(SemaRef, LHS, const_cast<Expr *>(getRHS())); + } + + ExprResult recreateBinOp(Sema &SemaRef, ExprResult LHS, ExprResult RHS) { + 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 +144,18 @@ bool Sema::CheckConstraintExpression(const Expr *ConstraintExpression, } template <typename AtomicEvaluator> -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 +166,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, // 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 LHSRes.isUsable() ? BO.recreateBinOp(S, LHSRes) : ExprEmpty(); if (BO.isAnd() && !IsLHSSatisfied) // [temp.constr.op] p2 @@ -151,12 +175,21 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, // 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 LHSRes.isUsable() ? BO.recreateBinOp(S, LHSRes) : ExprEmpty(); - return calculateConstraintSatisfaction( + ExprResult RHSRes = calculateConstraintSatisfaction( S, BO.getRHS(), Satisfaction, std::forward<AtomicEvaluator>(Evaluator)); + if (RHSRes.isInvalid()) + return ExprError(); + + if (!LHSRes.isUsable() || !RHSRes.isUsable()) + return ExprEmpty(); + return BO.recreateBinOp(S, LHSRes, RHSRes); } else if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) { - return calculateConstraintSatisfaction(S, C->getSubExpr(), Satisfaction, + // 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<AtomicEvaluator>(Evaluator)); } @@ -164,11 +197,11 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, 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 +218,7 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, << SubstitutedAtomicExpr.get()->getSourceRange(); for (const PartialDiagnosticAt &PDiag : EvaluationDiags) S.Diag(PDiag.first, PDiag.second); - return true; + return ExprError(); } assert(EvalResult.Val.isInt() && @@ -195,13 +228,13 @@ calculateConstraintSatisfaction(Sema &S, const Expr *ConstraintExpr, Satisfaction.Details.emplace_back(ConstraintExpr, SubstitutedAtomicExpr.get()); - return false; + return SubstitutedAtomicExpr; } -static bool calculateConstraintSatisfaction( - Sema &S, const NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs, - SourceLocation TemplateNameLoc, MultiLevelTemplateArgumentList &MLTAL, - const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { +static ExprResult calculateConstraintSatisfaction( + Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc, + const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr, + ConstraintSatisfaction &Satisfaction) { return calculateConstraintSatisfaction( S, ConstraintExpr, Satisfaction, [&](const Expr *AtomicExpr) { EnterExpressionEvaluationContext ConstantEvaluated( @@ -219,8 +252,8 @@ static bool calculateConstraintSatisfaction( return ExprError(); // We do not want error diagnostics escaping here. Sema::SFINAETrap Trap(S); - SubstitutedExpression = S.SubstExpr(const_cast<Expr *>(AtomicExpr), - MLTAL); + SubstitutedExpression = + S.SubstConstraintExpr(const_cast<Expr *>(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 @@ -268,72 +301,92 @@ static bool calculateConstraintSatisfaction( }); } -static bool CheckConstraintSatisfaction(Sema &S, const NamedDecl *Template, - ArrayRef<const Expr *> ConstraintExprs, - ArrayRef<TemplateArgument> TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { +static bool CheckConstraintSatisfaction( + Sema &S, const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs, + llvm::SmallVectorImpl<Expr *> &Converted, + const MultiLevelTemplateArgumentList &TemplateArgsList, + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) { if (ConstraintExprs.empty()) { Satisfaction.IsSatisfied = true; return false; } - for (auto& Arg : TemplateArgs) - if (Arg.isInstantiationDependent()) { - // No need to check satisfaction for dependent constraint expressions. - Satisfaction.IsSatisfied = true; - return false; - } + if (TemplateArgsList.isAnyArgInstantiationDependent()) { + // No need to check satisfaction for dependent constraint expressions. + Satisfaction.IsSatisfied = true; + return false; + } - Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), + ArrayRef<TemplateArgument> TemplateArgs = + TemplateArgsList.getNumSubstitutedLevels() > 0 + ? TemplateArgsList.getOutermost() + : ArrayRef<TemplateArgument>{}; + + Sema::InstantiatingTemplate Inst( + S, TemplateIDRange.getBegin(), Sema::InstantiatingTemplate::ConstraintsCheck{}, const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange); if (Inst.isInvalid()) return true; - MultiLevelTemplateArgumentList MLTAL; - MLTAL.addOuterTemplateArguments(TemplateArgs); - for (const Expr *ConstraintExpr : ConstraintExprs) { - if (calculateConstraintSatisfaction(S, Template, TemplateArgs, - TemplateIDRange.getBegin(), MLTAL, - ConstraintExpr, Satisfaction)) + ExprResult Res = calculateConstraintSatisfaction( + S, Template, TemplateIDRange.getBegin(), TemplateArgsList, + 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<const Expr *> ConstraintExprs, - ArrayRef<TemplateArgument> TemplateArgs, SourceRange TemplateIDRange, - ConstraintSatisfaction &OutSatisfaction) { + llvm::SmallVectorImpl<Expr *> &ConvertedConstraints, + const MultiLevelTemplateArgumentList &TemplateArgsList, + SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) { if (ConstraintExprs.empty()) { OutSatisfaction.IsSatisfied = true; return false; } if (!Template) { return ::CheckConstraintSatisfaction(*this, nullptr, ConstraintExprs, - TemplateArgs, TemplateIDRange, - OutSatisfaction); + ConvertedConstraints, TemplateArgsList, + TemplateIDRange, OutSatisfaction); } + + // A list of the template argument list flattened in a predictible manner for + // the purposes of caching. The ConstraintSatisfaction type is in AST so it + // has no access to the MultiLevelTemplateArgumentList, so this has to happen + // here. + llvm::SmallVector<TemplateArgument, 4> FlattenedArgs; + for (ArrayRef<TemplateArgument> List : TemplateArgsList) + FlattenedArgs.insert(FlattenedArgs.end(), List.begin(), List.end()); + llvm::FoldingSetNodeID ID; - ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs); + ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs); + void *InsertPos; if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) { OutSatisfaction = *Cached; return false; } auto Satisfaction = - std::make_unique<ConstraintSatisfaction>(Template, TemplateArgs); + std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs); if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, - TemplateArgs, TemplateIDRange, - *Satisfaction)) { + ConvertedConstraints, TemplateArgsList, + TemplateIDRange, *Satisfaction)) { return true; } OutSatisfaction = *Satisfaction; @@ -347,20 +400,111 @@ bool Sema::CheckConstraintSatisfaction( bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, ConstraintSatisfaction &Satisfaction) { return calculateConstraintSatisfaction( - *this, ConstraintExpr, Satisfaction, - [](const Expr *AtomicExpr) -> ExprResult { - return ExprResult(const_cast<Expr *>(AtomicExpr)); - }); + *this, ConstraintExpr, Satisfaction, + [](const Expr *AtomicExpr) -> ExprResult { + return ExprResult(const_cast<Expr *>(AtomicExpr)); + }) + .isInvalid(); +} + +bool Sema::SetupConstraintScope( + FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> 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<TemplateArgument>{}, + 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; + } + } else 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<TemplateArgument>{}, + 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<MultiLevelTemplateArgumentList> +Sema::SetupConstraintCheckingTemplateArgumentsAndScope( + FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> 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()) { + // 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<DeclContext>( + const_cast<FunctionDecl *>(FD)->getNonClosureContext())}; + LocalInstantiationScope Scope(*this, true); + llvm::Optional<MultiLevelTemplateArgumentList> MLTAL = + SetupConstraintCheckingTemplateArgumentsAndScope( + const_cast<FunctionDecl *>(FD), {}, Scope); + Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { @@ -371,14 +515,27 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD, // 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<Expr *, 1> 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 diff icult, 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<FunctionDecl *>(FD)->setTrailingRequiresClause(Converted[0]); + return false; } bool Sema::EnsureTemplateArgumentListConstraints( - TemplateDecl *TD, ArrayRef<TemplateArgument> TemplateArgs, + TemplateDecl *TD, MultiLevelTemplateArgumentList TemplateArgs, SourceRange TemplateIDRange) { ConstraintSatisfaction Satisfaction; llvm::SmallVector<const Expr *, 3> AssociatedConstraints; @@ -391,7 +548,8 @@ bool Sema::EnsureTemplateArgumentListConstraints( SmallString<128> TemplateArgString; TemplateArgString = " "; TemplateArgString += getTemplateArgumentBindingsText( - TD->getTemplateParameters(), TemplateArgs.data(), TemplateArgs.size()); + TD->getTemplateParameters(), TemplateArgs.getInnermost().data(), + TemplateArgs.getInnermost().size()); Diag(TemplateIDRange.getBegin(), diag::err_template_arg_list_constraints_not_satisfied) @@ -423,21 +581,13 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( Sema::ContextRAII savedContext(*this, Decl); LocalInstantiationScope Scope(*this); - // 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<MultiLevelTemplateArgumentList> MLTAL = + SetupConstraintCheckingTemplateArgumentsAndScope(Decl, TemplateArgs, + Scope); + + if (!MLTAL) + return true; + Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast<CXXMethodDecl>(Decl)) { @@ -445,7 +595,8 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( Record = Method->getParent(); } CXXThisScopeRAII ThisScope(*this, Record, ThisQuals, Record != nullptr); - return CheckConstraintSatisfaction(Template, TemplateAC, TemplateArgs, + llvm::SmallVector<Expr *, 1> Converted; + return CheckConstraintSatisfaction(Template, TemplateAC, Converted, *MLTAL, PointOfInstantiation, Satisfaction); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 6e45fdfbeb089..4db70aa10f68c 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -992,6 +992,82 @@ static bool checkArgPlaceholdersForOverload(Sema &S, MultiExprArg Args, return false; } +static bool FriendMembersDifferByConstraints(Sema &S, DeclContext *CurContext, + FunctionDecl *Old, + FunctionDecl *New) { + // Only necessary/valid if we're instantiating a + // ClassTemplateSpecializationDecl. + if (CurContext->getDeclKind() != Decl::ClassTemplateSpecialization) + return false; + + // Only when we're dealing with a friend function. + if (!Old->getFriendObjectKind() || !New->getFriendObjectKind()) + return false; + + // If the the two functions share lexical declaration context, they are not in + // separate instantations. + if (New->getLexicalDeclContext() == Old->getLexicalDeclContext()) + return false; + + auto *OldLexCtx = + dyn_cast<ClassTemplateSpecializationDecl>(Old->getLexicalDeclContext()); + auto *NewLexCtx = + dyn_cast<ClassTemplateSpecializationDecl>(New->getLexicalDeclContext()); + + if (!OldLexCtx || !NewLexCtx) + return false; + + auto InitAssocConstrs = [](FunctionDecl *FD, + SmallVectorImpl<const Expr *> &AC) { + if (const auto *FT = FD->getDescribedFunctionTemplate()) + FT->getAssociatedConstraints(AC); + else + FD->getAssociatedConstraints(AC); + }; + SmallVector<const Expr *, 3> OldAC; + SmallVector<const Expr *, 3> NewAC; + InitAssocConstrs(Old, OldAC); + InitAssocConstrs(New, NewAC); + + assert(OldAC.size() == NewAC.size() && + "Should not have been identical unless constraints were the same?"); + + // At this point, we know the constraints lists should be the same. + auto *OldBegin = OldAC.begin(); + auto *NewBegin = NewAC.begin(); + + auto SubstConstraint = [](Sema &S, ClassTemplateSpecializationDecl *LexCtx, + const Expr *Constraint) { + Sema::ContextRAII SavedContext(S, LexCtx); + LocalInstantiationScope Scope(S); + EnterExpressionEvaluationContext EvalCtx( + S, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + Sema::SFINAETrap Trap(S); + return S.SubstConstraintExpr(const_cast<Expr *>(Constraint), + S.getTemplateInstantiationArgs(LexCtx)); + }; + + for (; OldBegin != OldAC.end(); ++OldBegin, ++NewBegin) { + ExprResult OldConverted = SubstConstraint(S, OldLexCtx, *OldBegin); + ExprResult NewConverted = SubstConstraint(S, NewLexCtx, *NewBegin); + + // We can consider these diff erent, since they depend on the instantiation, + // and cannot prove they are identical. + if (OldConverted.isInvalid() || NewConverted.isInvalid()) + return true; + + // profile & compare. If they are now diff erent, they aren't equal. + llvm::FoldingSetNodeID NewID, OldID; + NewConverted.get()->Profile(NewID, S.getASTContext(), /*Canonical=*/true); + OldConverted.get()->Profile(OldID, S.getASTContext(), /*Canonical=*/true); + if (NewID != OldID) + return true; + } + + // If we haven't found a diff ering constraint, these are the same. + return false; +} + /// Determine whether the given New declaration is an overload of the /// declarations in Old. This routine returns Ovl_Match or Ovl_NonFunction if /// New and Old cannot be overloaded, e.g., if New has the same signature as @@ -1068,6 +1144,20 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old, !shouldLinkPossiblyHiddenDecl(*I, New)) continue; + // If this is a friend function currently being instantiated as a part + // of a ClassTemplateSpecializationDecl, it could have + // otherwise-identical-looking constraints that depend on the current + // instantiation. In this case, if the otherwise apparent 'match' and + // the new declaration diff er by lexical declaration context (meaning + // diff erent Class Template Specializations), AND one of the collected + // constraints seems to 'depend' on the current instantiation in some + // way, than these are not matches and are likely instead overloads. + // Note that an 'error' case might still be valid later (as it could be + // something that would be 'changed' at 'checking' time), but is proof + // we depend on the Class Template Specialization. + if (FriendMembersDifferByConstraints(*this, CurContext, OldF, New)) + continue; + Match = *I; return Ovl_Match; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 4a42969c34d3f..34e1352de2c53 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4702,9 +4702,11 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, bool AreArgsDependent = TemplateSpecializationType::anyDependentTemplateArguments(*TemplateArgs, Converted); + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Converted); if (!AreArgsDependent && CheckConstraintSatisfaction( - NamedConcept, {NamedConcept->getConstraintExpr()}, Converted, + NamedConcept, {NamedConcept->getConstraintExpr()}, MLTAL, SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(), TemplateArgs->getRAngleLoc()), Satisfaction)) @@ -5564,6 +5566,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, if (Inst.isInvalid()) return true; + ConstraintEvalRAII EvalRAII(*this); TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, Converted); Params = SubstTemplateParams(Params, CurContext, MultiLevelTemplateArgumentList(TemplateArgs)); @@ -5921,13 +5924,20 @@ bool Sema::CheckTemplateArgumentList( if (UpdateArgsWithConversions) TemplateArgs = std::move(NewArgs); - if (!PartialTemplateArgs && - EnsureTemplateArgumentListConstraints( - Template, Converted, SourceRange(TemplateLoc, - TemplateArgs.getRAngleLoc()))) { - if (ConstraintsNotSatisfied) - *ConstraintsNotSatisfied = true; - return true; + if (!PartialTemplateArgs) { + TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack, + Converted); + MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs( + Template, &StackTemplateArgs, /*RelativeToPrimary*/ true, + /*Pattern*/ nullptr, + /*LookBeyondLambda*/ true, /*IncludeContainingStruct*/ true); + if (EnsureTemplateArgumentListConstraints( + Template, MLTAL, + SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) { + if (ConstraintsNotSatisfied) + *ConstraintsNotSatisfied = true; + return true; + } } return false; @@ -7457,7 +7467,9 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, // are not considered. if (ParamsAC.empty()) return false; + Template->getAssociatedConstraints(TemplateAC); + bool IsParamAtLeastAsConstrained; if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, IsParamAtLeastAsConstrained)) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 0662dd7602236..4fa1395b62b71 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2791,8 +2791,10 @@ CheckDeducedArgumentConstraints(Sema& S, TemplateDeclT *Template, TemplateDeductionInfo& Info) { llvm::SmallVector<const Expr *, 3> AssociatedConstraints; Template->getAssociatedConstraints(AssociatedConstraints); - if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, - DeducedArgs, Info.getLocation(), + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(DeducedArgs); + if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL, + Info.getLocation(), Info.AssociatedConstraintsSatisfaction) || !Info.AssociatedConstraintsSatisfaction.IsSatisfied) { Info.reset(TemplateArgumentList::CreateCopy(S.Context, DeducedArgs)); @@ -4572,8 +4574,11 @@ CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs, /*PartialTemplateArgs=*/false, Converted)) return Sema::DAR_FailedAlreadyDiagnosed; + + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Converted); if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, - Converted, TypeLoc.getLocalSourceRange(), + MLTAL, TypeLoc.getLocalSourceRange(), Satisfaction)) return Sema::DAR_FailedAlreadyDiagnosed; if (!Satisfaction.IsSatisfied) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 4fd9d003e66a1..a82ac32dd81f9 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -55,9 +55,18 @@ using namespace sema; /// 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; @@ -153,11 +162,13 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( 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?"); } @@ -174,10 +185,18 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( } } else if (const auto *Rec = dyn_cast<CXXRecordDecl>(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<InjectedClassNameType>(RecordType) + ->getInjectedSpecializationType(); + const auto *InjectedType = cast<TemplateSpecializationType>(Injected); + Result.addOuterTemplateArguments(InjectedType->template_arguments()); + } } } @@ -2327,6 +2346,18 @@ bool Sema::SubstTypeConstraint( const MultiLevelTemplateArgumentList &TemplateArgs) { const ASTTemplateArgumentListInfo *TemplArgInfo = TC->getTemplateArgsAsWritten(); + + // If we're not checking a constraint, we shouldn't be instantiating the type + // constraint, so we should just create a copy of the previous one. + // TODO: ERICH: Should this be RebuildExprInCurrentInstantiation here? + if (!IsEvaluatingAConstraint()) { + Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), TC->getNamedConcept(), + TC->getNamedConcept(), TemplArgInfo, + TC->getImmediatelyDeclaredConstraint()); + return false; + } + TemplateArgumentListInfo InstArgs; if (TemplArgInfo) { @@ -3511,6 +3542,14 @@ Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) { return Instantiator.TransformExpr(E); } +ExprResult +Sema::SubstConstraintExpr(Expr *E, + const MultiLevelTemplateArgumentList &TemplateArgs) { + + ConstraintEvalRAII EvalRAII(*this); + return SubstExpr(E, TemplateArgs); +} + ExprResult Sema::SubstInitializer(Expr *Init, const MultiLevelTemplateArgumentList &TemplateArgs, bool CXXDirectInit) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 77c5677b65c87..e3460d5d4cb25 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2062,19 +2062,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( 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 @@ -2182,6 +2170,11 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( // definition. We don't want non-template functions to be marked as being // template instantiations. Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); + } else if (!isFriend) { + // If this is not a function template, and this is not a friend (that is, + // this is a locally declared function), save the instantiation relationship + // for the purposes of constraint instantiation. + Function->setInstantiatedFromDecl(D); } if (isFriend) { @@ -2420,23 +2413,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( 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<CXXRecordDecl>(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) { @@ -2454,6 +2430,9 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( if (!DC) return nullptr; } + CXXRecordDecl *Record = cast<CXXRecordDecl>(DC); + Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + DeclarationNameInfo NameInfo = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs); @@ -2461,7 +2440,6 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( adjustForRewrite(FunctionRewriteKind, D, T, TInfo, NameInfo); // Build the instantiated method declaration. - CXXRecordDecl *Record = cast<CXXRecordDecl>(DC); CXXMethodDecl *Method = nullptr; SourceLocation StartLoc = D->getInnerLocStart(); @@ -2768,9 +2746,6 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl( // 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)) return nullptr; } @@ -4013,18 +3988,7 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) { 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(), diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index aef757efc9e71..2717e4abf5b84 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -12997,13 +12997,6 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { 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 @@ -13038,7 +13031,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { E->getCallOperator()->getEndLoc(), NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().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 index 86a9c7733069e..4b9c83b9d1f91 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -941,6 +941,10 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { case FunctionDecl::TK_NonTemplate: mergeRedeclarable(FD, Redecl); break; + case FunctionDecl::TK_DependentNonTemplate: + mergeRedeclarable(FD, Redecl); + FD->setInstantiatedFromDecl(readDeclAs<FunctionDecl>()); + break; case FunctionDecl::TK_FunctionTemplate: // Merged when we merge the template. FD->setDescribedFunctionTemplate(readDeclAs<FunctionTemplateDecl>()); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 3666d5a6daab8..49401c1d5e4b8 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -585,6 +585,9 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { switch (D->getTemplatedKind()) { case FunctionDecl::TK_NonTemplate: break; + case FunctionDecl::TK_DependentNonTemplate: + Record.AddDeclRef(D->getInstantiatedFromDecl()); + break; case FunctionDecl::TK_FunctionTemplate: Record.AddDeclRef(D->getDescribedFunctionTemplate()); break; 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 index dd6bfe8011ddf..15e00e4481e75 100644 --- 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 @@ struct D { }; static_assert(C<int>{}); // expected-note{{while checking constraint satisfaction for template 'C<int>' required here}} static_assert(D<int>{}); // expected-note{{while checking constraint satisfaction for template 'D<int>' 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 <unsigned I> +struct AAA { + template <typename T> + 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<int>::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/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp index 23c79421bb6f8..8b3ca7fdcaf65 100644 --- a/clang/test/SemaTemplate/concepts.cpp +++ b/clang/test/SemaTemplate/concepts.cpp @@ -256,3 +256,447 @@ C auto **j1 = g(); // expected-error {{deduced type 'int' does not satisfy 'C' C auto **&j2 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'}} C auto **&&j3 = g(); // expected-error {{deduced type 'int' does not satisfy 'C'}} } + +namespace DeferredInstantiationInstScope { +template <typename T> +struct remove_ref { + using type = T; +}; +template <typename T> +struct remove_ref<T &> { + using type = T; +}; +template <typename T> +struct remove_ref<T &&> { + using type = T; +}; + +template <typename T> +constexpr bool IsInt = PR54443::is_same<typename remove_ref<T>::type, + int>::value; + +template <typename U> +void SingleDepthReferencesTop(U &&u) { + struct lc { + void operator()() // #SDRT_OP + requires IsInt<decltype(u)> // #SDRT_REQ + {} + }; + lc lv; + lv(); // #SDRT_CALL +} + +template <typename U> +void SingleDepthReferencesTopNotCalled(U &&u) { + struct lc { + void operator()() + requires IsInt<typename decltype(u)::FOO> + {} + }; + lc lv; +} + +template <typename U> +void SingleDepthReferencesTopCalled(U &&u) { + struct lc { + void operator()() // #CALLOP + requires IsInt<typename decltype(u)::FOO> // #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 <typename U> +void SingleDepthReferencesTopLambda(U &&u) { + []() + requires IsInt<decltype(u)> + {}(); +} + +template <typename U> +void DoubleDepthReferencesTop(U &&u) { + struct lc { // #DDRT_STRCT + void operator()() { + struct lc2 { + void operator()() // #DDRT_OP + requires IsInt<decltype(u)> // #DDRT_REQ + {} + }; + lc2 lv2; + lv2(); // #DDRT_CALL + } + }; + lc lv; + lv(); +} + +template <typename U> +void DoubleDepthReferencesTopLambda(U &&u) { + []() { []() + requires IsInt<decltype(u)> + {}(); }(); +} + +template <typename U> +void DoubleDepthReferencesAll(U &&u) { + struct lc { // #DDRA_STRCT + void operator()(U &&u2) { + struct lc2 { + void operator()(U &&u3) // #DDRA_OP + requires IsInt<decltype(u)> && // #DDRA_REQ + IsInt<decltype(u2)> && IsInt<decltype(u3)> + {} + }; + lc2 lv2; + lv2(u2); // #DDRA_CALL + } + }; + lc lv; + lv(u); +} + +template <typename U> +void DoubleDepthReferencesAllLambda(U &&u) { + [](U &&u2) { + [](U && u3) + requires IsInt<decltype(u)> && + IsInt<decltype(u2)> && IsInt<decltype(u3)> + {}(u2); + }(u); +} + +template <typename U> +void HasInnerFunc(U &&u) { + void InnerFunc(U && u2) + requires IsInt<decltype(u)> && // #INNERFUNC_REQ + IsInt<decltype(u2)>; + InnerFunc(u); // #INNERFUNC_CALL +} + +template <typename U> +struct CausesFriendConstraint { + template <typename V> + friend void FriendFunc(CausesFriendConstraint, V) // #FF_DECL + requires IsInt<U> && + IsInt<V> // #FF_REQ + {} +}; +// FIXME: Re-enable this test when constraints are allowed to refer to captures. +// template<typename T> +// void ChecksCapture(T x) { +// [y = x]() requires(IsInt<decltype(y)>){}(); +// } + +template <typename T> +void ChecksLocalVar(T x) { + T Local; + []() + requires(IsInt<decltype(Local)>) + {}(); +} + +template <typename T> +void LocalStructMemberVar(T x) { + struct S { + T local; + void foo() + requires(IsInt<decltype(local)>) // #LSMV_REQ + {} + } s; + s.foo(); // #LSMV_CALL +}; + +template <typename T> +struct ChecksMemberVar { + T t; + void foo() + requires(IsInt<decltype(t)>) // #CMV_FOO + {} + template <typename U> + void foo2() // #CMV_FOO2 + requires(IsInt<decltype(t)>) // #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<decltype(u)>' 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<decltype(u)>' 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<decltype(u)>' evaluated to false}} + + DoubleDepthReferencesAllLambda(v); + // FIXME: This should error on constraint failure! (Lambda!) + DoubleDepthReferencesAllLambda(will_fail); + HasInnerFunc(v); + HasInnerFunc(will_fail); + // expected-error@#INNERFUNC_CALL{{invalid reference to function 'InnerFunc': constraints not satisfied}} + // expected-note@-2{{in instantiation of function template specialization}} + // expected-note@#INNERFUNC_REQ{{'IsInt<decltype(u)>' evaluated to false}} + + CausesFriendConstraint<int> 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<double>' 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 'IsInt<decltype(this->local)>' evaluated to false}} + + ChecksMemberVar<int> CMV; + CMV.foo(); + CMV.foo2<int>(); + + ChecksMemberVar<float> CMV2; + CMV2.foo(); + // expected-error@-1{{invalid reference to function 'foo'}} + // expected-note@#CMV_FOO{{because 'IsInt<decltype(this->t)>' evaluated to false}} + CMV2.foo2<float>(); + // expected-error@-1{{no matching member function for call to 'foo2'}} + // expected-note@#CMV_FOO2{{constraints not satisfied}} + // expected-note@#CMV_FOO2_REQ{{because 'IsInt<decltype(this->t)>' evaluated to false}} +} +} // namespace DeferredInstantiationInstScope + +namespace LibCXXOperatorRedef { +template <typename T, typename U> struct is_same { + static constexpr bool value = false; +}; +template <typename T> struct is_same<T, T> { + static constexpr bool value = false; +}; + +template <typename T, typename U> +concept same_as = is_same<T, U>::value; + +// An issue found from libcxx when trying to commit the deferred concepts patch. +// This caused an error of 'redefinition of funcN'. +template <class _Tp> struct __range_adaptor_closure { + template <typename _View, typename _Closure> + requires same_as<_Tp, _Closure> + friend constexpr decltype(auto) R1func1(_View &&__view, + _Closure &&__closure){}; + template <typename _View, typename _Closure> + friend constexpr decltype(auto) R1func2(_View &&__view, + _Closure &&__closure) + requires same_as<_Tp, _Closure> + {}; + template <same_as<_Tp> _View, typename _Closure> + friend constexpr decltype(auto) R1func3(_View &&__view, + _Closure &&__closure) + {}; +}; + +struct A : __range_adaptor_closure<A> {}; +struct B : __range_adaptor_closure<B> {}; + +// These three fail because after the 1st pass of instantiation, they are still +// identical. +template <class _Tp> struct __range_adaptor_closure2 { + template <typename _View, typename _Closure> + requires same_as<_View, _Closure> + friend constexpr decltype(auto) R2func1(_View &&__view, // #FUNC1 + _Closure &&__closure){}; + template <typename _View, typename _Closure> + friend constexpr decltype(auto) R2func2(_View &&__view, // #FUNC2 + _Closure &&__closure) + requires same_as<_View, _Closure> + {}; + template <typename _View, same_as<_View> _Closure> + friend constexpr decltype(auto) R2func3(_View &&__view, // #FUNC3 + _Closure &&__closure){}; +}; + +struct A2 : __range_adaptor_closure2<A2> {}; +struct B2 : __range_adaptor_closure2<B2> {}; +// 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 diff erent despite ::type not being valid. +template <class _Tp> struct __range_adaptor_closure3 { + template <typename _View, typename _Closure> + requires same_as<typename _Tp::type, _Closure> + friend constexpr decltype(auto) R3func1(_View &&__view, + _Closure &&__closure){}; + template <typename _View, typename _Closure> + friend constexpr decltype(auto) R3func2(_View &&__view, + _Closure &&__closure) + requires same_as<typename _Tp::type, _Closure> + {}; + template <same_as<typename _Tp::type> _View, typename _Closure> + friend constexpr decltype(auto) R3func3(_View &&__view, + _Closure &&__closure) + {}; +}; + +struct A3 : __range_adaptor_closure3<A3> {}; +struct B3 : __range_adaptor_closure3<B3> {}; + +template <class _Tp> struct __range_adaptor_closure4 { + template <typename _View, typename _Closure> + requires same_as<_Tp, _View> + // expected-note@+1{{previous definition is here}} + void foo1(_View &&, _Closure &&) {} + template <typename _View, typename _Closure> + requires same_as<_Tp, _View> + // expected-error@+1{{class member cannot be redeclared}} + void foo1(_View &&, _Closure &&) {} + + template <typename _View, typename _Closure> + // expected-note@+1{{previous definition is here}} + void foo2(_View &&, _Closure &&) + requires same_as<_Tp, _View> + {} + template <typename _View, typename _Closure> + // expected-error@+1{{class member cannot be redeclared}} + void foo2(_View &&, _Closure &&) + requires same_as<_Tp, _View> + {} + + template <same_as<_Tp> _View, typename _Closure> + // expected-note@+1{{previous definition is here}} + void foo3(_View &&, _Closure &&) + {} + template <same_as<_Tp> _View, typename _Closure> + // expected-error@+1{{class member cannot be redeclared}} + void foo3(_View &&, _Closure &&) + {} +}; + +// Requires instantiation to fail, so no errors here. +template<class _Tp> struct __range_adaptor_closure5 { + template<same_as<_Tp> U> + friend void foo(){} + template<same_as<_Tp> U> + friend void foo(){} +}; + +template<class _Tp> struct __range_adaptor_closure6 { + template<same_as<_Tp> U> + friend void foo(){} // #RAC6FOO1 + template<same_as<_Tp> U> + friend void foo(){} // #RAC6FOO2 +}; +struct A6 : __range_adaptor_closure6<A6> {}; +//expected-error@#RAC6FOO2{{redefinition of 'foo'}} +//expected-note@-2{{in instantiation of template class}} +//expected-note@#RAC6FOO1{{previous definition is here}} + +template<class T> struct S1 { + template<typename U> + friend void dupe(){} // #S1DUPE + + template<typename U> + requires same_as<U, U> + friend void dupe2(){} // #S1DUPE2 +}; +template<class T> struct S2 { + template<typename U> + friend void dupe(){} // #S2DUPE + + template<typename U> + requires same_as<U, U> + friend void dupe2(){} // #S2DUPE2 +}; + +template<class T> struct S3 { + template<typename U> + requires same_as<T, U> + friend void dupe(){} // #S3DUPE +}; +template<class T> struct S4 { + template<typename U> + requires same_as<T, U> + friend void dupe(){} // #S4DUPE +}; + +// Same as S3 and S4, but aren't instantiated with the same T. +template<class T> struct S5 { + template<typename U> + requires same_as<T, U> + friend void not_dupe(){} +}; +template<class T> struct S6 { + template<typename U> + requires same_as<T, U> + friend void not_dupe(){} +}; + +template<class T> struct S7 { + void not_dupe() requires same_as<T, T>{} +}; + +void useS() { + S1<int> s1; + S2<double> 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}} + + S3<int> s3; + S4<int> s4; + // expected-error@#S4DUPE{{redefinition}} + // expected-note@-2{{in instantiation of template class}} + // expected-note@#S3DUPE{{previous definition is here}} + + // OK, because only instantiated with diff erent T. + S5<int> s5; + S6<double> s6; + + S7<int> s7; +} + +} // namespace LibCXXOperatorRedef diff --git a/clang/test/SemaTemplate/deferred-concept-inst.cpp b/clang/test/SemaTemplate/deferred-concept-inst.cpp new file mode 100644 index 0000000000000..00e8845c3ecbb --- /dev/null +++ b/clang/test/SemaTemplate/deferred-concept-inst.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify -Wno-unused-value +// expected-no-diagnostics + +namespace GithubBug44178 { +template <typename D> +struct CRTP { + void call_foo() + requires requires(D &v) { v.foo(); } + { + static_cast<D *>(this)->foo(); + } +}; + +struct Test : public CRTP<Test> { + 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 index 73dd20d27d226..3f438d8885a20 100644 --- 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 <typename... Args> requires ((sizeof(Args) == 1), ...) // expected-note@-1 {{because '(sizeof(int) == 1) , (sizeof(char) == 1) , (sizeof(int) == 1)' evaluated to false}} @@ -40,6 +40,20 @@ struct S { static_assert(S<void>::f(1)); +// Similar to the 'S' test, but tries to use 'U' in the requires clause. +template <typename T2> +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 <typename U> + 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<void>::f(1)); + constexpr auto value = 0; template<typename T> diff --git a/clang/test/SemaTemplate/trailing-return-short-circuit.cpp b/clang/test/SemaTemplate/trailing-return-short-circuit.cpp new file mode 100644 index 0000000000000..0d1c9b52b0e85 --- /dev/null +++ b/clang/test/SemaTemplate/trailing-return-short-circuit.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s + +template <class T> + requires(sizeof(T) > 2) || T::value // #FOO_REQ +void Foo(T){}; // #FOO + +template <class T> +void TrailingReturn(T) // #TRAILING + requires(sizeof(T) > 2) || // #TRAILING_REQ + T::value // #TRAILING_REQ_VAL +{}; +template <bool B> +struct HasValue { + static constexpr bool value = B; +}; +static_assert(sizeof(HasValue<true>) <= 2); + +template <bool B> +struct HasValueLarge { + static constexpr bool value = B; + int I; +}; +static_assert(sizeof(HasValueLarge<true>) > 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<true>{}); + TrailingReturn(HasValue<true>{}); + + // Passes the 1st check, but would have passed the 2nd one. + Foo(HasValueLarge<true>{}); + TrailingReturn(HasValueLarge<true>{}); + + // 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<false>{}); + // expected-error@-1 {{no matching function for call to 'Foo'}} + // expected-note@#FOO{{candidate template ignored: constraints not satisfied [with T = HasValue<false>]}} + // expected-note@#FOO_REQ{{because 'sizeof(HasValue<false>) > 2' (1 > 2) evaluated to false}} + // expected-note@#FOO_REQ{{and 'HasValue<false>::value' evaluated to false}} + TrailingReturn(HasValue<false>{}); + // expected-error@-1 {{no matching function for call to 'TrailingReturn'}} + // expected-note@#TRAILING{{candidate template ignored: constraints not satisfied [with T = HasValue<false>]}} + // expected-note@#TRAILING_REQ{{because 'sizeof(HasValue<false>) > 2' (1 > 2) evaluated to false}} + // expected-note@#TRAILING_REQ_VAL{{and 'HasValue<false>::value' evaluated to false}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits