diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -153,6 +153,10 @@ - A new builtin type trait ``__is_trivially_equaltiy_comparable`` has been added, which checks whether comparing two instances of a type is equivalent to ``memcmp(&lhs, &rhs, sizeof(T)) == 0``. +- Clang now implements `[temp.deduct]p9`, subsitution failures inside lambdas from + unevaluated contexts will be surfaced as errors. They were previously handled as + SFINAE. + New Compiler Flags ------------------ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9257,6 +9257,9 @@ /// a TemplateDecl. DeducedTemplateArgumentSubstitution, + /// We are substituting into a lambda expression. + LambdaExpressionSubstitution, + /// We are substituting prior template arguments into a new /// template parameter. The template parameter itself is either a /// NonTypeTemplateParmDecl or a TemplateTemplateParmDecl. diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -372,6 +372,8 @@ return "ExplicitTemplateArgumentSubstitution"; case CodeSynthesisContext::DeducedTemplateArgumentSubstitution: return "DeducedTemplateArgumentSubstitution"; + case CodeSynthesisContext::LambdaExpressionSubstitution: + return "LambdaExpressionSubstitution"; case CodeSynthesisContext::PriorTemplateArgumentSubstitution: return "PriorTemplateArgumentSubstitution"; case CodeSynthesisContext::DefaultTemplateArgumentChecking: diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -34,6 +34,7 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/TemplateInstCallback.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TimeProfiler.h" #include @@ -367,6 +368,7 @@ case InitializingStructuredBinding: case MarkingClassDllexported: case BuildingBuiltinDumpStructCall: + case LambdaExpressionSubstitution: return false; // This function should never be called when Kind's value is Memoization. @@ -959,6 +961,8 @@ break; case CodeSynthesisContext::Memoization: + case CodeSynthesisContext::LambdaExpressionSubstitution: + // FIXME: add a note for lambdas. break; case CodeSynthesisContext::ConstraintsCheck: { @@ -1016,6 +1020,7 @@ if (InNonInstantiationSFINAEContext) return std::optional(nullptr); + bool SawLambdaSubstitution = false; for (SmallVectorImpl::const_reverse_iterator Active = CodeSynthesisContexts.rbegin(), ActiveEnd = CodeSynthesisContexts.rend(); @@ -1037,6 +1042,15 @@ case CodeSynthesisContext::NestedRequirementConstraintsCheck: // This is a template instantiation, so there is no SFINAE. return std::nullopt; + case CodeSynthesisContext::LambdaExpressionSubstitution: + // [temp.deduct]p9 + // A lambda-expression appearing in a function type or a template + // parameter is not considered part of the immediate context for the + // purposes of template argument deduction. + + // We need to check parents. + SawLambdaSubstitution = true; + break; case CodeSynthesisContext::DefaultTemplateArgumentInstantiation: case CodeSynthesisContext::PriorTemplateArgumentSubstitution: @@ -1049,12 +1063,17 @@ case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution: case CodeSynthesisContext::DeducedTemplateArgumentSubstitution: + // We're either substituting explicitly-specified template arguments, + // deduced template arguments. SFINAE applies unless we are in a lambda + // expression, see [temp.deduct]p9. + if (SawLambdaSubstitution) + return std::nullopt; + [[fallthrough]]; case CodeSynthesisContext::ConstraintSubstitution: case CodeSynthesisContext::RequirementInstantiation: case CodeSynthesisContext::RequirementParameterInstantiation: - // We're either substituting explicitly-specified template arguments, - // deduced template arguments, a constraint expression or a requirement - // in a requires expression, so SFINAE applies. + // SFINAE always applies in a constraint expression or a requirement + // in a requires expression. assert(Active->DeductionInfo && "Missing deduction info pointer"); return Active->DeductionInfo; @@ -1343,6 +1362,14 @@ ExprResult TransformLambdaExpr(LambdaExpr *E) { LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); Sema::ConstraintEvalRAII RAII(*this); + + Sema::CodeSynthesisContext C; + C.Kind = clang::Sema::CodeSynthesisContext::LambdaExpressionSubstitution; + C.PointOfInstantiation = E->getBeginLoc(); + SemaRef.pushCodeSynthesisContext(C); + auto PopCtx = + llvm::make_scope_exit([this] { SemaRef.popCodeSynthesisContext(); }); + ExprResult Result = inherited::TransformLambdaExpr(E); if (Result.isInvalid()) return Result; diff --git a/clang/test/CXX/temp/temp.deduct/p9.cpp b/clang/test/CXX/temp/temp.deduct/p9.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/temp/temp.deduct/p9.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +template +auto f(T) -> decltype([]() { T::invalid; } ()); +void f(...); +void test_f() { + f(0); // expected-error@-3 {{type 'int' cannot be used prior to '::'}} + // expected-note@-1 {{while substituting deduced template arguments}} +} + +template +void g(T); +void g(...); +void test_g() { + g(0); // expected-error@-4 {{type 'int' cannot be used prior to '::'}} + // expected-note@-4 {{in instantiation of default argument}} + // expected-note@-2 {{while substituting deduced template arguments}} +} + +template +auto h(T) -> decltype([x = T::invalid]() { }); +void h(...); +void test_h() { + h(0); // expected-error@-3 {{type 'int' cannot be used prior to '::'}} + // expected-note@-1 {{while substituting deduced template arguments}} +} + +template +auto i(T) -> decltype([]() -> typename T::invalid { }); +void i(...); +void test_i() { + i(0); // expected-error@-3 {{type 'int' cannot be used prior to '::'}} + // expected-note@-1 {{while substituting deduced template arguments}} +} + +template +auto j(T t) -> decltype([](auto x) -> decltype(x.invalid) { } (t)); // #1 +void j(...); // #2 +void test_j() { + j(0); +} diff --git a/clang/test/SemaCXX/cxx1y-init-captures.cpp b/clang/test/SemaCXX/cxx1y-init-captures.cpp --- a/clang/test/SemaCXX/cxx1y-init-captures.cpp +++ b/clang/test/SemaCXX/cxx1y-init-captures.cpp @@ -31,7 +31,7 @@ } - void h(int i, char c) { g(i, c); } //expected-note{{in instantiation}} + void h(int i, char c) { g(i, c); } //expected-note 2{{in instantiation}} } namespace odr_use_within_init_capture { @@ -164,7 +164,7 @@ return 0; } -int run = test(); //expected-note {{instantiation}} +int run = test(); //expected-note 2 {{instantiation}} } diff --git a/clang/test/SemaCXX/lambda-expressions.cpp b/clang/test/SemaCXX/lambda-expressions.cpp --- a/clang/test/SemaCXX/lambda-expressions.cpp +++ b/clang/test/SemaCXX/lambda-expressions.cpp @@ -263,7 +263,7 @@ // expected-note 4 {{capture 'ts' by}} } template void nested2(int); // ok - template void nested2(int, int); // expected-note {{in instantiation of}} + template void nested2(int, int); // expected-note 2 {{in instantiation of}} } namespace PR13860 { diff --git a/clang/test/SemaCXX/lambda-unevaluated.cpp b/clang/test/SemaCXX/lambda-unevaluated.cpp --- a/clang/test/SemaCXX/lambda-unevaluated.cpp +++ b/clang/test/SemaCXX/lambda-unevaluated.cpp @@ -28,8 +28,10 @@ template auto g(T) -> decltype([]() { T::invalid; } ()); -auto e = g(0); // expected-error{{no matching function for call}} -// expected-note@-2 {{substitution failure}} +auto e = g(0); // expected-error@-1{{type 'int' cannot be used prior to '::'}} + // expected-note@-1{{while substituting deduced template}} + // expected-error@-2 {{no matching function for call to 'g'}} + // expected-note@-4 {{substitution failure}} template auto foo(decltype([] { @@ -146,3 +148,34 @@ namespace lambda_in_trailing_decltype { auto x = ([](auto) -> decltype([] {}()) {}(0), 2); } + +namespace lambda_in_constraints { +struct WithFoo { static void foo(); }; + +template +concept lambda_works = requires { + []() { T::foo(); }; +}; + +static_assert(!lambda_works); +static_assert(lambda_works); + +template +int* func(T) requires requires { []() { T::foo(); }; }; +double* func(...); + +static_assert(__is_same(decltype(func(0)), double*)); +static_assert(__is_same(decltype(func(WithFoo())), int*)); + +template +auto direct_lambda(T) -> decltype([] { T::foo(); }) {} +void direct_lambda(...) {} + +void recursive() { + direct_lambda(0); // expected-error@-4 {{type 'int' cannot be used prior to '::'}} + // expected-note@-1 {{while substituting deduced template arguments}} + bool x = requires { direct_lambda(0); }; // expected-error@-6 {{type 'int' cannot be used prior to '::'}} + // expected-note@-1 {{while substituting deduced template arguments}} + +} +} diff --git a/clang/test/SemaCXX/warn-unused-lambda-capture.cpp b/clang/test/SemaCXX/warn-unused-lambda-capture.cpp --- a/clang/test/SemaCXX/warn-unused-lambda-capture.cpp +++ b/clang/test/SemaCXX/warn-unused-lambda-capture.cpp @@ -189,7 +189,7 @@ } void test_use_template() { - test_templated(); // expected-note{{in instantiation of function template specialization 'test_templated' requested here}} + test_templated(); // expected-note 13{{in instantiation of function template specialization 'test_templated' requested here}} } namespace pr35555 { diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1024,11 +1024,7 @@ Lambdas in unevaluated contexts P0315R4 - -
Partial - temp.deduct/9 is not implemented yet. -
- + Clang 17