Skip to content

release/19.x: [Clang][Sema] Revisit the fix for the lambda within a type alias template decl (#89934) #106166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 38 additions & 35 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/PrettyDeclStackTrace.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeVisitor.h"
Expand Down Expand Up @@ -87,12 +88,19 @@ struct Response {
// than lambda classes.
const FunctionDecl *
getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) {
if (!isLambdaCallOperator(LambdaCallOperator))
return LambdaCallOperator;
while (true) {
if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
LambdaCallOperator->getDescribedTemplate());
FTD && FTD->getInstantiatedFromMemberTemplate()) {
LambdaCallOperator =
FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
} else if (LambdaCallOperator->getPrimaryTemplate()) {
// Cases where the lambda operator is instantiated in
// TemplateDeclInstantiator::VisitCXXMethodDecl.
LambdaCallOperator =
LambdaCallOperator->getPrimaryTemplate()->getTemplatedDecl();
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
->getInstantiatedFromMemberFunction())
LambdaCallOperator = Prev;
Expand Down Expand Up @@ -138,22 +146,28 @@ getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) {
// Check if we are currently inside of a lambda expression that is
// surrounded by a using alias declaration. e.g.
// template <class> using type = decltype([](auto) { ^ }());
// By checking if:
// 1. The lambda expression and the using alias declaration share the
// same declaration context.
// 2. They have the same template depth.
// We have to do so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never
// a DeclContext, nor does it have an associated specialization Decl from which
// we could collect these template arguments.
bool isLambdaEnclosedByTypeAliasDecl(
const FunctionDecl *PrimaryLambdaCallOperator,
const FunctionDecl *LambdaCallOperator,
const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) {
return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext())
->getTemplateDepth() ==
PrimaryTypeAliasDecl->getTemplateDepth() &&
getLambdaAwareParentOfDeclContext(
const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) ==
PrimaryTypeAliasDecl->getDeclContext();
struct Visitor : RecursiveASTVisitor<Visitor> {
Visitor(const FunctionDecl *CallOperator) : CallOperator(CallOperator) {}
bool VisitLambdaExpr(const LambdaExpr *LE) {
// Return true to bail out of the traversal, implying the Decl contains
// the lambda.
return getPrimaryTemplateOfGenericLambda(LE->getCallOperator()) !=
CallOperator;
}
const FunctionDecl *CallOperator;
};

QualType Underlying =
PrimaryTypeAliasDecl->getTemplatedDecl()->getUnderlyingType();

return !Visitor(getPrimaryTemplateOfGenericLambda(LambdaCallOperator))
.TraverseType(Underlying);
}

// Add template arguments from a variable template instantiation.
Expand Down Expand Up @@ -290,23 +304,8 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,

// If this function is a generic lambda specialization, we are done.
if (!ForConstraintInstantiation &&
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
// TypeAliasTemplateDecls should be taken into account, e.g.
// when we're deducing the return type of a lambda.
//
// template <class> int Value = 0;
// template <class T>
// using T = decltype([]<int U = 0>() { return Value<T>; }());
//
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
if (isLambdaEnclosedByTypeAliasDecl(
/*PrimaryLambdaCallOperator=*/getPrimaryTemplateOfGenericLambda(
Function),
/*PrimaryTypeAliasDecl=*/TypeAlias.PrimaryTypeAliasDecl))
return Response::UseNextDecl(Function);
}
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
return Response::Done();
}

} else if (Function->getDescribedFunctionTemplate()) {
assert(
Expand Down Expand Up @@ -418,10 +417,9 @@ Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
// Retrieve the template arguments for a using alias declaration.
// This is necessary for constraint checking, since we always keep
// constraints relative to the primary template.
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
const FunctionDecl *PrimaryLambdaCallOperator =
getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator());
if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator,
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef);
ForConstraintInstantiation && TypeAlias) {
if (isLambdaEnclosedByTypeAliasDecl(Rec->getLambdaCallOperator(),
TypeAlias.PrimaryTypeAliasDecl)) {
Result.addOuterTemplateArguments(TypeAlias.Template,
TypeAlias.AssociatedTemplateArguments,
Expand Down Expand Up @@ -1642,12 +1640,17 @@ namespace {

CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
auto &CCS = SemaRef.CodeSynthesisContexts.back();
if (CCS.Kind ==
Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
if (auto TypeAlias =
TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
getSema());
TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments)
if (TA.isDependent())
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
}
return inherited::ComputeLambdaDependency(LSI);
}
Expand Down
75 changes: 72 additions & 3 deletions clang/test/SemaTemplate/alias-template-with-lambdas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,84 @@ void bar() {

namespace GH82104 {

template <typename, typename...> int Zero = 0;
template <typename, typename... D> constexpr int Value = sizeof...(D);

template <typename T, typename...U>
using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }());
template <typename T, typename... U>
using T14 = decltype([]<int V = 0>(auto Param) {
return Value<T, U...> + V + (int)sizeof(Param);
}("hello"));

template <typename T> using T15 = T14<T, T>;

static_assert(__is_same(T15<char>, int));

// FIXME: This still crashes because we can't extract template arguments T and U
// outside of the instantiation context of T16.
#if 0
template <typename T, typename... U>
using T16 = decltype([](auto Param) requires (sizeof(Param) != 1 && sizeof...(U) > 0) {
return Value<T, U...> + sizeof(Param);
});
static_assert(T16<int, char, float>()(42) == 2 + sizeof(42));
#endif
} // namespace GH82104

namespace GH89853 {

template <typename = void>
static constexpr auto innocuous = []<int m> { return m; };

template <auto Pred = innocuous<>>
using broken = decltype(Pred.template operator()<42>());

broken<> *boom;

template <auto Pred =
[]<char c> {
(void)static_cast<char>(c);
}>
using broken2 = decltype(Pred.template operator()<42>());

broken2<> *boom2;

template <auto Pred = []<char m> { return m; }>
using broken3 = decltype(Pred.template operator()<42>());

broken3<> *boom3;

static constexpr auto non_default = []<char c>(True auto) {
(void) static_cast<char>(c);
};

template<True auto Pred>
using broken4 = decltype(Pred.template operator()<42>(Pred));

broken4<non_default>* boom4;

} // namespace GH89853

namespace GH105885 {

template<int>
using test = decltype([](auto...) {
}());

static_assert(__is_same(test<0>, void));

} // namespace GH105885

namespace GH102760 {

auto make_tuple = []< class Tag, class... Captures>(Tag, Captures...) {
return []< class _Fun >( _Fun) -> void requires requires { 0; }
{};
};

template < class, class... _As >
using Result = decltype(make_tuple(0)(_As{}...));

using T = Result<int, int>;

} // namespace GH102760

} // namespace lambda_calls
Loading