mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 08:16:47 +00:00
[Clang][Sema] Revisit the fix for the lambda within a type alias template decl (#89934)
In the last patch #82310, we used template depths to tell if such alias decls contain lambdas, which is wrong because the lambda can also appear as a part of the default argument, and that would make `getTemplateInstantiationArgs` provide extra template arguments in undesired contexts. This leads to issue #89853. Moreover, our approach for https://github.com/llvm/llvm-project/issues/82104 was sadly wrong. We tried to teach `DeduceReturnType` to consider alias template arguments; however, giving these arguments in the context where they should have been substituted in a `TransformCallExpr` call is never correct. This patch addresses such problems by using a `RecursiveASTVisitor` to check if the lambda is contained by an alias `Decl`, as well as twiddling the lambda dependencies - we should also build a dependent lambda expression if the surrounding alias template arguments were dependent. Fixes #89853 Fixes #102760 Fixes #105885
This commit is contained in:
parent
87157ab0f6
commit
b412ec5d39
@ -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"
|
||||
@ -88,12 +89,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;
|
||||
@ -139,22 +147,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.
|
||||
@ -293,23 +307,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(
|
||||
@ -421,10 +420,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,
|
||||
@ -1647,12 +1645,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);
|
||||
}
|
||||
|
@ -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…
x
Reference in New Issue
Block a user