erichkeane updated this revision to Diff 427359. erichkeane added a comment.
Updated to include the fix for the libcxx issue, which was that we weren't properly differentiating between 'friend' functions based on concepts. I'll likely be submitting this early monday or so, but would like to give @ChuanqiXu a chance to check out the changes. This is the same as my 'last' committed version of this patch, plus the changes I split out into here for easier review: https://reviews.llvm.org/D125020 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/SemaDecl.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/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,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}} +} Index: clang/test/SemaTemplate/instantiate-requires-clause.cpp =================================================================== --- clang/test/SemaTemplate/instantiate-requires-clause.cpp +++ 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 @@ 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> 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,447 @@ 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 different 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 different T. + S5<int> s5; + S6<double> s6; + + S7<int> s7; +} + +} // namespace LibCXXOperatorRedef 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 @@ -941,6 +941,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 @@ -12997,13 +12997,6 @@ NewCallOpType); } - // Transform the trailing requires clause - ExprResult NewTrailingRequiresClause; - if (Expr *TRC = E->getCallOperator()->getTrailingRequiresClause()) - // FIXME: Concepts: Substitution into requires clause should only happen - // when checking satisfaction. - NewTrailingRequiresClause = getDerived().TransformExpr(TRC); - // Create the local class that will describe the lambda. // FIXME: DependencyKind below is wrong when substituting inside a templated @@ -13038,7 +13031,7 @@ E->getCallOperator()->getEndLoc(), NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>().getParams(), E->getCallOperator()->getConstexprKind(), - NewTrailingRequiresClause.get()); + E->getCallOperator()->getTrailingRequiresClause()); LSI->CallOperator = NewCallOperator; Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2062,19 +2062,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 @@ -2182,6 +2170,11 @@ // 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 @@ 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 @@ if (!DC) return nullptr; } + CXXRecordDecl *Record = cast<CXXRecordDecl>(DC); + Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); + DeclarationNameInfo NameInfo = SemaRef.SubstDeclarationNameInfo(D->getNameInfo(), TemplateArgs); @@ -2461,7 +2440,6 @@ 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 @@ // 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 @@ 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,18 @@ 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) { @@ -3488,6 +3519,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 @@ -4702,9 +4702,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)) @@ -5564,6 +5566,7 @@ if (Inst.isInvalid()) return true; + ConstraintEvalRAII EvalRAII(*this); TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, Converted); Params = SubstTemplateParams(Params, CurContext, MultiLevelTemplateArgumentList(TemplateArgs)); @@ -5921,13 +5924,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; @@ -7457,7 +7467,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/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -992,6 +992,82 @@ 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 different, 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 different, 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 differing 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 @@ !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 differ by lexical declaration context (meaning + // different 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; } Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -11177,6 +11177,7 @@ switch (CheckOverload(S, NewFD, Previous, OldDecl, /*NewIsUsingDecl*/ false)) { case Ovl_Match: + // TODO: Could we figure out if the constraints mismatch here? Redeclaration = true; break; 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,25 @@ 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 @@ } 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 @@ // 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 @@ // 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 @@ 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 @@ << 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 @@ 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 @@ 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 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(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 @@ // 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; @@ -391,7 +548,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) @@ -423,21 +581,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)) { @@ -445,7 +595,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 @@ -303,6 +303,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,8 +3729,12 @@ 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 @@ } 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 { 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 @@ -75,6 +75,9 @@ 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 @@ 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 @@ 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 @@ 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. Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -6989,7 +6989,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); @@ -7019,8 +7046,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 @@ -7030,7 +7059,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 @@ -7066,9 +7128,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. @@ -8802,7 +8864,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 @@ -9523,6 +9586,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 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 { Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -1890,7 +1890,9 @@ 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 @@ /// 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 @@ 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 @@ /// 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 @@ -304,6 +304,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