erichkeane updated this revision to Diff 423713. erichkeane added a comment. Herald added a subscriber: martong. Herald added a reviewer: shafik.
Fixed the issue that Corentin's test came up with, added a number of others. The problem is that we didn't properly 'collect' the parameters in cases where we referred to a parameter of a function that contains the current function, so this patch did a bit of refactoring to make sure we did so recursively. Additionally, we found an additional case (the non-functor case) where we didn't have a way to get the primary template, so we added a new entry in the FunctionDecl's PointerUnion to handle these cases. Please review! I'd love to get this into trunk with time to bake before the branch. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D119544/new/ https://reviews.llvm.org/D119544 Files: 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/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/deferred-concept-inst.cpp clang/test/SemaTemplate/instantiate-requires-clause.cpp clang/test/SemaTemplate/trailing-return-short-circuit.cpp
Index: clang/test/SemaTemplate/trailing-return-short-circuit.cpp =================================================================== --- /dev/null +++ clang/test/SemaTemplate/trailing-return-short-circuit.cpp @@ -0,0 +1,61 @@ +// 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}} +} Index: clang/test/SemaTemplate/instantiate-requires-clause.cpp =================================================================== --- clang/test/SemaTemplate/instantiate-requires-clause.cpp +++ clang/test/SemaTemplate/instantiate-requires-clause.cpp @@ -40,6 +40,18 @@ 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@+2 {{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> Index: clang/test/SemaTemplate/deferred-concept-inst.cpp =================================================================== --- /dev/null +++ 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 Index: clang/test/SemaTemplate/concepts.cpp =================================================================== --- clang/test/SemaTemplate/concepts.cpp +++ clang/test/SemaTemplate/concepts.cpp @@ -256,3 +256,97 @@ 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> +constexpr bool constraint = true; +template <typename T> +constexpr bool false_constraint = false; + +template <typename U> +void SingleDepthReferencesTop(U &&u) { + struct lc { + void operator()() requires constraint<decltype(u)> {} + }; + lc lv; + lv(); +} + +template<typename U> +void SingleDepthReferencesTopLambda(U &&u) { + []() requires constraint<decltype(u)> {}(); +} + +template <typename U> +void DoubleDepthReferencesTop(U &&u) { + struct lc { + void operator()() { + struct lc2 { + void operator()() requires constraint<decltype(u)>{} + }; + lc2 lv2; + lv2(); + } + }; + lc lv; + lv(); +} + +template<typename U> +void DoubleDepthReferencesTopLambda(U &&u) { + [](){[]() requires constraint<decltype(u)> {}(); }(); +} + +template <typename U> +void DoubleDepthReferencesAll(U &&u) { + struct lc { + void operator()(U &&u2) { + struct lc2 { + void operator()(U &&u3) requires constraint<decltype(u)> && + constraint<decltype(u2)> && constraint<decltype(u3)> {} + }; + lc2 lv2; + lv2(u2); + } + }; + lc lv; + lv(u); +} + +template<typename U> +void DoubleDepthReferencesAllLambda(U &&u) { + [](U &&u2) { + [](U && u3) requires constraint<decltype(u)> && + constraint<decltype(u2)> && constraint<decltype(u3)> {} + (u2); + }(u); +} + +template<typename U> +void HasInnerFunc(U &&u) { + void InnerFunc(U && u2) requires constraint<decltype(u)> && + constraint<decltype(u2)>; + InnerFunc(u); +} + +template <typename U> +struct CausesFriendConstraint { + template <typename V> + friend void FriendFunc(CausesFriendConstraint, V) requires constraint<U> && + constraint<V> {} +}; + +void test_dependent() { + int v = 0; + SingleDepthReferencesTop(v); + SingleDepthReferencesTopLambda(v); + DoubleDepthReferencesTop(v); + DoubleDepthReferencesTopLambda(v); + DoubleDepthReferencesAll(v); + DoubleDepthReferencesAllLambda(v); + HasInnerFunc(v); + + CausesFriendConstraint<int> CFC; + FriendFunc(CFC, 1); +} +} // namespace DeferredInstantiationInstScope Index: clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp =================================================================== --- clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp +++ clang/test/CXX/temp/temp.constr/temp.constr.constr/non-function-templates.cpp @@ -90,3 +90,24 @@ 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 Index: clang/lib/Serialization/ASTWriterDecl.cpp =================================================================== --- clang/lib/Serialization/ASTWriterDecl.cpp +++ clang/lib/Serialization/ASTWriterDecl.cpp @@ -585,6 +585,9 @@ 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; Index: clang/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- clang/lib/Serialization/ASTReaderDecl.cpp +++ clang/lib/Serialization/ASTReaderDecl.cpp @@ -940,6 +940,10 @@ 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>()); Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -12998,11 +12998,8 @@ } // 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); + ExprResult NewTrailingRequiresClause = + E->getCallOperator()->getTrailingRequiresClause(); // Create the local class that will describe the lambda. Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2051,19 +2051,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 @@ -2171,6 +2159,8 @@ // definition. We don't want non-template functions to be marked as being // template instantiations. Function->setInstantiationOfMemberFunction(D, TSK_ImplicitInstantiation); + } else if (!isFriend) { + Function->setInstantiatedFromDecl(D); } if (isFriend) { @@ -2409,22 +2399,7 @@ 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) { @@ -2757,9 +2732,6 @@ // 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; } @@ -4002,18 +3974,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(), Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -55,9 +55,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; @@ -153,11 +162,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?"); } @@ -174,10 +185,18 @@ } } 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()); + } } } @@ -2304,6 +2323,17 @@ 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. + if (!IsEvaluatingAConstraint()) { + Inst->setTypeConstraint(TC->getNestedNameSpecifierLoc(), + TC->getConceptNameInfo(), TC->getNamedConcept(), + TC->getNamedConcept(), TemplArgInfo, + TC->getImmediatelyDeclaredConstraint()); + return false; + } + TemplateArgumentListInfo InstArgs; if (TemplArgInfo) { @@ -3488,6 +3518,14 @@ 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) { Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2791,8 +2791,10 @@ 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 @@ 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) { Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -4706,9 +4706,11 @@ 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)) @@ -5568,6 +5570,7 @@ if (Inst.isInvalid()) return true; + ConstraintEvalRAII EvalRAII(*this); TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, Converted); Params = SubstTemplateParams(Params, CurContext, MultiLevelTemplateArgumentList(TemplateArgs)); @@ -5925,13 +5928,20 @@ 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; @@ -7461,7 +7471,9 @@ // are not considered. if (ParamsAC.empty()) return false; + Template->getAssociatedConstraints(TemplateAC); + bool IsParamAtLeastAsConstrained; if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, IsParamAtLeastAsConstrained)) Index: clang/lib/Sema/SemaConcept.cpp =================================================================== --- clang/lib/Sema/SemaConcept.cpp +++ clang/lib/Sema/SemaConcept.cpp @@ -30,6 +30,7 @@ namespace { class LogicalBinOp { + SourceLocation Loc; OverloadedOperatorKind Op = OO_None; const Expr *LHS = nullptr; const Expr *RHS = nullptr; @@ -40,12 +41,14 @@ 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,22 @@ 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?"); + assert(LHS.isUsable() && RHS.isUsable() && "Side not usable?"); + // 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 +141,18 @@ } 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 +163,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 LHSRes.isUsable() ? BO.recreateBinOp(S, LHSRes) : ExprEmpty(); if (BO.isAnd() && !IsLHSSatisfied) // [temp.constr.op] p2 @@ -151,12 +172,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 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 +194,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 +215,7 @@ << SubstitutedAtomicExpr.get()->getSourceRange(); for (const PartialDiagnosticAt &PDiag : EvaluationDiags) S.Diag(PDiag.first, PDiag.second); - return true; + return ExprError(); } assert(EvalResult.Val.isInt() && @@ -195,13 +225,13 @@ 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 +249,8 @@ 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,56 +298,70 @@ }); } -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; + } + + ArrayRef<TemplateArgument> TemplateArgs = + TemplateArgsList.getNumSubstitutedLevels() > 0 + ? TemplateArgsList.getOutermost() + : ArrayRef<TemplateArgument>{}; - Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(), + 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) - // [temp.constr.op] p2 + + Converted.push_back(Res.get()); + if (!Satisfaction.IsSatisfied) { + // Backfill the 'converted' list with nulls so we can keep the Converted + Converted.append(ConstraintExprs.size() - Converted.size(), nullptr); + // and unconverted lists in sync. [temp.constr.op] p2 // [...] To determine if a conjunction is satisfied, the satisfaction // of the first operand is checked. If that is not satisfied, the // conjunction is not satisfied. [...] return false; + } } return false; } bool Sema::CheckConstraintSatisfaction( const NamedDecl *Template, 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; } + ArrayRef<TemplateArgument> TemplateArgs = + TemplateArgsList.getNumSubstitutedLevels() > 0 + ? TemplateArgsList.getOutermost() + : ArrayRef<TemplateArgument>{}; + llvm::FoldingSetNodeID ID; void *InsertPos; ConstraintSatisfaction *Satisfaction = nullptr; @@ -334,8 +378,8 @@ Satisfaction = &OutSatisfaction; } if (::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, - TemplateArgs, TemplateIDRange, - *Satisfaction)) { + ConvertedConstraints, TemplateArgsList, + TemplateIDRange, *Satisfaction)) { if (ShouldCache) delete Satisfaction; return true; @@ -353,20 +397,138 @@ 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 (TemplateArgs) { + MultiLevelTemplateArgumentList JustTemplArgs( + *FD->getTemplateSpecializationArgs()); + if (addInstantiatedParametersToScope( + FD, PrimaryTemplate->getTemplatedDecl(), Scope, JustTemplArgs)) + return true; + } else { + // In the case where we are recursively adding functions, we can be in a + // place where we need the parameters from this function relative to the + // innermost-level, despite it not being originated from a function that + // was a template. + if (addInstantiatedParametersToScope( + FD, PrimaryTemplate->getTemplatedDecl(), Scope, MLTAL)) + 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) { + InstantiatingTemplate Inst(*this, FD->getPointOfInstantiation(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, + FD->getInstantiatedFromMemberFunction(), + 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, FD->getInstantiatedFromMemberFunction(), Scope, MLTAL)) + return true; + } else if (FD->getTemplatedKind() == FunctionDecl::TK_DependentNonTemplate) { + + InstantiatingTemplate Inst(*this, FD->getPointOfInstantiation(), + Sema::InstantiatingTemplate::ConstraintsCheck{}, + FD->getInstantiatedFromDecl(), + TemplateArgs ? *TemplateArgs + : ArrayRef<TemplateArgument>{}, + SourceRange()); + if (Inst.isInvalid()) + return true; + // case where this is a function declared inside of another. + if (addInstantiatedParametersToScope( + FD, FD->getInstantiatedFromDecl(), Scope, MLTAL)) + return true; + } + + // Make sure we recursively add the ParmVarDecls from the containing function. + if (DeclContext *ParentFunc = FD->getParentFunctionOrMethod()) { + return SetupConstraintScope(cast<FunctionDecl>(ParentFunc), TemplateArgs, + MLTAL, Scope); + } else if (DeclContext *ParentFunc = FD->getLexicalParentFunctionOrMethod()) { + // In the case of functions-declared-in-functions, the DeclContext is the + // TU, so make sure we get the LEXICAL decl context in this case. + return SetupConstraintScope(cast<FunctionDecl>(ParentFunc), TemplateArgs, + MLTAL, Scope); + } + + 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; } + + LocalInstantiationScope Scope(*this); + llvm::Optional<MultiLevelTemplateArgumentList> MLTAL = + SetupConstraintCheckingTemplateArgumentsAndScope( + const_cast<FunctionDecl *>(FD), {}, Scope); + Qualifiers ThisQuals; CXXRecordDecl *Record = nullptr; if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) { @@ -377,14 +539,27 @@ // 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 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<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; @@ -397,7 +572,8 @@ 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) @@ -429,21 +605,13 @@ 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)) { @@ -451,7 +619,8 @@ 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); } Index: clang/lib/ExtractAPI/ExtractAPIConsumer.cpp =================================================================== --- clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -161,6 +161,7 @@ // Skip templated functions. switch (Decl->getTemplatedKind()) { case FunctionDecl::TK_NonTemplate: + case FunctionDecl::TK_DependentNonTemplate: break; case FunctionDecl::TK_MemberSpecialization: case FunctionDecl::TK_FunctionTemplateSpecialization: Index: clang/lib/AST/DeclBase.cpp =================================================================== --- clang/lib/AST/DeclBase.cpp +++ clang/lib/AST/DeclBase.cpp @@ -293,6 +293,16 @@ 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 //===----------------------------------------------------------------------===// Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -3729,6 +3729,8 @@ FunctionDecl::TemplatedKind FunctionDecl::getTemplatedKind() const { if (TemplateOrSpecialization.isNull()) return TK_NonTemplate; + if (TemplateOrSpecialization.is<FunctionDecl *>()) + return TK_DependentNonTemplate; if (TemplateOrSpecialization.is<FunctionTemplateDecl *>()) return TK_FunctionTemplate; if (TemplateOrSpecialization.is<MemberSpecializationInfo *>()) @@ -3780,6 +3782,16 @@ TemplateOrSpecialization = Template; } +void FunctionDecl::setInstantiatedFromDecl(FunctionDecl *FD) { + assert(TemplateOrSpecialization.isNull() && + "Member function is already a specialization"); + TemplateOrSpecialization = FD; +} + +FunctionDecl *FunctionDecl::getInstantiatedFromDecl() const { + return TemplateOrSpecialization.dyn_cast<FunctionDecl *>(); +} + bool FunctionDecl::isImplicitlyInstantiable() const { // If the function is invalid, it can't be implicitly instantiated. if (isInvalidDecl()) Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -3109,6 +3109,11 @@ 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(); Index: clang/include/clang/Sema/Template.h =================================================================== --- clang/include/clang/Sema/Template.h +++ clang/include/clang/Sema/Template.h @@ -158,6 +158,14 @@ 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 +205,9 @@ const ArgList &getInnermost() const { return TemplateArgumentLists.front(); } + + /// Retrieve the outermost template argument list. + const ArgList &getOutermost() const { return TemplateArgumentLists.back(); } }; /// The context in which partial ordering of function templates occurs. Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -7007,7 +7007,34 @@ 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); @@ -7037,8 +7064,10 @@ /// 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 @@ -7048,7 +7077,40 @@ /// 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 @@ -7084,9 +7146,9 @@ /// /// \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. @@ -8819,7 +8881,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 @@ -9540,6 +9603,11 @@ 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. Index: clang/include/clang/AST/DeclBase.h =================================================================== --- clang/include/clang/AST/DeclBase.h +++ clang/include/clang/AST/DeclBase.h @@ -906,6 +906,14 @@ const_cast<const Decl*>(this)->getParentFunctionOrMethod()); } + /// Does the same thing as getParentFunctionOrMethod, except starts withthe + /// 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 { Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -1887,7 +1887,9 @@ TK_FunctionTemplateSpecialization, // A function template specialization that hasn't yet been resolved to a // particular specialized function template. - TK_DependentFunctionTemplateSpecialization + TK_DependentFunctionTemplateSpecialization, + // A Dependent function that itself is not a function. + TK_DependentNonTemplate }; /// Stashed information about a defaulted function definition whose body has @@ -1936,20 +1938,21 @@ /// 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 *, + /// For non-templates, this value will be NULL, unless this was instantiated + /// as an inner-declared function in another function template, which will + /// cause this to have a pointer to a FunctionDecl. 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<FunctionDecl *, FunctionTemplateDecl *, MemberSpecializationInfo *, FunctionTemplateSpecializationInfo *, DependentFunctionTemplateSpecializationInfo *> - TemplateOrSpecialization; + TemplateOrSpecialization; /// Provides source/type location info for the declaration name embedded in /// the DeclaratorDecl base class. @@ -2685,6 +2688,10 @@ setInstantiationOfMemberFunction(getASTContext(), FD, TSK); } + /// Specify the function that this was instantiated from, despite it not, + /// itself being a template. + void setInstantiatedFromDecl(FunctionDecl *FD); + /// Retrieves the function template that is described by this /// function declaration. /// @@ -2699,6 +2706,8 @@ /// FunctionTemplateDecl from a FunctionDecl. FunctionTemplateDecl *getDescribedFunctionTemplate() const; + FunctionDecl *getInstantiatedFromDecl() const; + void setDescribedFunctionTemplate(FunctionTemplateDecl *Template); /// Determine whether this function is a function template Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -245,6 +245,10 @@ `Issue 54578 <https://github.com/llvm/llvm-project/issues/54578>`_. - 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
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits