diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -67,6 +67,14 @@ - ``float.h`` now exposes (in hosted mode) extensions made available from the AIX system header. +- Unevaluated lambdas in dependant contexts no longer result in clang crashing. + This fixes Issues `50376 < https://github.com/llvm/llvm-project/issues/50376>`_, + `54296 < https://github.com/llvm/llvm-project/issues/54296>`_, + `51414 < https://github.com/llvm/llvm-project/issues/51414>`_, + `51416 < https://github.com/llvm/llvm-project/issues/51416>`_, + and `51641 < https://github.com/llvm/llvm-project/issues/51641>`_. + + Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -275,6 +275,14 @@ SMF_All = 0x3f }; +public: + enum LambdaDependencyKind { + LDK_Unknown = 0, + LDK_AlwaysDependent, + LDK_NeverDependent, + }; + +private: struct DefinitionData { #define FIELD(Name, Width, Merge) \ unsigned Name : Width; @@ -374,7 +382,7 @@ /// lambda will have been created with the enclosing context as its /// declaration context, rather than function. This is an unfortunate /// artifact of having to parse the default arguments before. - unsigned Dependent : 1; + unsigned DependencyKind : 2; /// Whether this lambda is a generic lambda. unsigned IsGenericLambda : 1; @@ -408,9 +416,9 @@ /// The type of the call method. TypeSourceInfo *MethodTyInfo; - LambdaDefinitionData(CXXRecordDecl *D, TypeSourceInfo *Info, bool Dependent, + LambdaDefinitionData(CXXRecordDecl *D, TypeSourceInfo *Info, unsigned DK, bool IsGeneric, LambdaCaptureDefault CaptureDefault) - : DefinitionData(D), Dependent(Dependent), IsGenericLambda(IsGeneric), + : DefinitionData(D), DependencyKind(DK), IsGenericLambda(IsGeneric), CaptureDefault(CaptureDefault), NumCaptures(0), NumExplicitCaptures(0), HasKnownInternalLinkage(0), ManglingNumber(0), MethodTyInfo(Info) { @@ -547,8 +555,14 @@ bool DelayTypeCreation = false); static CXXRecordDecl *CreateLambda(const ASTContext &C, DeclContext *DC, TypeSourceInfo *Info, SourceLocation Loc, - bool DependentLambda, bool IsGeneric, + unsigned DependencyKind, bool IsGeneric, + LambdaCaptureDefault CaptureDefault); + // Clang 14 ABI Compatibility + static CXXRecordDecl *CreateLambda(const ASTContext &C, DeclContext *DC, + TypeSourceInfo *Info, SourceLocation Loc, + bool KnownDependent, bool IsGeneric, LambdaCaptureDefault CaptureDefault); + static CXXRecordDecl *CreateDeserialized(const ASTContext &C, unsigned ID); bool isDynamicClass() const { @@ -1774,7 +1788,17 @@ /// function declaration itself is dependent. This flag indicates when we /// know that the lambda is dependent despite that. bool isDependentLambda() const { - return isLambda() && getLambdaData().Dependent; + return isLambda() && getLambdaData().DependencyKind == LDK_AlwaysDependent; + } + + bool isNeverDependentLambda() const { + return isLambda() && getLambdaData().DependencyKind == LDK_NeverDependent; + } + + unsigned getLambdaDependencyKind() const { + if (!isLambda()) + return LDK_Unknown; + return getLambdaData().DependencyKind; } TypeSourceInfo *getLambdaTypeInfo() const { 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 @@ -6735,6 +6735,12 @@ void ActOnCXXExitDeclInitializer(Scope *S, Decl *Dcl); /// Create a new lambda closure type. + CXXRecordDecl *createLambdaClosureType(SourceRange IntroducerRange, + TypeSourceInfo *Info, + unsigned LambdaDependencyKind, + LambdaCaptureDefault CaptureDefault); + + /// Create a new lambda closure type - For Clang 14 ABI Compatibility. CXXRecordDecl *createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info, bool KnownDependent, diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -2863,10 +2863,14 @@ auto TInfoOrErr = import(DCXX->getLambdaTypeInfo()); if (!TInfoOrErr) return TInfoOrErr.takeError(); + using Fn = CXXRecordDecl *(*)(const ASTContext &, DeclContext *, + TypeSourceInfo *, SourceLocation, unsigned, + bool, LambdaCaptureDefault); if (GetImportedOrCreateSpecialDecl( - D2CXX, CXXRecordDecl::CreateLambda, D, Importer.getToContext(), - DC, *TInfoOrErr, Loc, DCXX->isDependentLambda(), - DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault())) + D2CXX, static_cast(&CXXRecordDecl::CreateLambda), D, + Importer.getToContext(), DC, *TInfoOrErr, Loc, + DCXX->getLambdaDependencyKind(), DCXX->isGenericLambda(), + DCXX->getLambdaCaptureDefault())) return D2CXX; ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl()); if (!CDeclOrErr) diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1161,6 +1161,8 @@ if (Record->isDependentLambda()) return true; + if (Record->isNeverDependentLambda()) + return false; } if (const auto *Function = dyn_cast(this)) { diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -146,20 +146,31 @@ CXXRecordDecl * CXXRecordDecl::CreateLambda(const ASTContext &C, DeclContext *DC, TypeSourceInfo *Info, SourceLocation Loc, - bool Dependent, bool IsGeneric, + unsigned DependencyKind, bool IsGeneric, LambdaCaptureDefault CaptureDefault) { auto *R = new (C, DC) CXXRecordDecl(CXXRecord, TTK_Class, C, DC, Loc, Loc, nullptr, nullptr); R->setBeingDefined(true); - R->DefinitionData = - new (C) struct LambdaDefinitionData(R, Info, Dependent, IsGeneric, - CaptureDefault); + R->DefinitionData = new (C) struct LambdaDefinitionData( + R, Info, DependencyKind, IsGeneric, CaptureDefault); R->setMayHaveOutOfDateDef(false); R->setImplicit(true); + C.getTypeDeclType(R, /*PrevDecl=*/nullptr); return R; } +CXXRecordDecl * +CXXRecordDecl::CreateLambda(const ASTContext &C, DeclContext *DC, + TypeSourceInfo *Info, SourceLocation Loc, + bool KnownDependent, bool IsGeneric, + LambdaCaptureDefault CaptureDefault) { + return CreateLambda( + C, DC, Info, Loc, + unsigned(KnownDependent ? LDK_AlwaysDependent : LDK_Unknown), IsGeneric, + CaptureDefault); +} + CXXRecordDecl * CXXRecordDecl::CreateDeserialized(const ASTContext &C, unsigned ID) { auto *R = new (C, ID) CXXRecordDecl( diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -238,26 +238,35 @@ return LSI->GLTemplateParameterList; } -CXXRecordDecl *Sema::createLambdaClosureType(SourceRange IntroducerRange, - TypeSourceInfo *Info, - bool KnownDependent, - LambdaCaptureDefault CaptureDefault) { +CXXRecordDecl * +Sema::createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info, + unsigned LambdaDependencyKind, + LambdaCaptureDefault CaptureDefault) { DeclContext *DC = CurContext; while (!(DC->isFunctionOrMethod() || DC->isRecord() || DC->isFileContext())) DC = DC->getParent(); bool IsGenericLambda = getGenericLambdaTemplateParameterList(getCurLambda(), *this); // Start constructing the lambda class. - CXXRecordDecl *Class = CXXRecordDecl::CreateLambda(Context, DC, Info, - IntroducerRange.getBegin(), - KnownDependent, - IsGenericLambda, - CaptureDefault); + CXXRecordDecl *Class = CXXRecordDecl::CreateLambda( + Context, DC, Info, IntroducerRange.getBegin(), LambdaDependencyKind, + IsGenericLambda, CaptureDefault); DC->addDecl(Class); return Class; } +CXXRecordDecl * +Sema::createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info, + bool KnownDependent, + LambdaCaptureDefault CaptureDefault) { + return createLambdaClosureType( + IntroducerRange, Info, + static_cast(KnownDependent ? CXXRecordDecl::LDK_AlwaysDependent + : CXXRecordDecl::LDK_Unknown), + CaptureDefault); +} + /// Determine whether the given context is or is enclosed in an inline /// function. static bool isInInlineFunction(const DeclContext *DC) { @@ -898,17 +907,18 @@ // Determine if we're within a context where we know that the lambda will // be dependent, because there are template parameters in scope. - bool KnownDependent; + CXXRecordDecl::LambdaDependencyKind LambdaDependencyKind = + CXXRecordDecl::LDK_Unknown; if (LSI->NumExplicitTemplateParams > 0) { auto *TemplateParamScope = CurScope->getTemplateParamParent(); assert(TemplateParamScope && "Lambda with explicit template param list should establish a " "template param scope"); assert(TemplateParamScope->getParent()); - KnownDependent = TemplateParamScope->getParent() - ->getTemplateParamParent() != nullptr; - } else { - KnownDependent = CurScope->getTemplateParamParent() != nullptr; + if (TemplateParamScope->getParent()->getTemplateParamParent() != nullptr) + LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent; + } else if (CurScope->getTemplateParamParent() != nullptr) { + LambdaDependencyKind = CXXRecordDecl::LDK_AlwaysDependent; } // Determine the signature of the call operator. @@ -977,8 +987,9 @@ UPPC_DeclarationType); } - CXXRecordDecl *Class = createLambdaClosureType(Intro.Range, MethodTyInfo, - KnownDependent, Intro.Default); + CXXRecordDecl *Class = createLambdaClosureType( + Intro.Range, MethodTyInfo, static_cast(LambdaDependencyKind), + Intro.Default); CXXMethodDecl *Method = startLambdaDefinition(Class, Intro.Range, MethodTyInfo, EndLoc, Params, ParamInfo.getDeclSpec().getConstexprSpecifier(), diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1831,7 +1831,7 @@ if (D->isLambda()) Record = CXXRecordDecl::CreateLambda( SemaRef.Context, Owner, D->getLambdaTypeInfo(), D->getLocation(), - D->isDependentLambda(), D->isGenericLambda(), + D->getLambdaDependencyKind(), D->isGenericLambda(), D->getLambdaCaptureDefault()); else Record = CXXRecordDecl::Create(SemaRef.Context, D->getTagKind(), Owner, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -12922,14 +12922,24 @@ NewTrailingRequiresClause = getDerived().TransformExpr(TRC); // Create the local class that will describe the lambda. - // FIXME: KnownDependent below is wrong when substituting inside a templated - // context that isn't a DeclContext (such as a variable template). + + // FIXME: DependencyKind below is wrong when substituting inside a templated + // context that isn't a DeclContext (such as a variable template), or when + // substituting an unevaluated lambda inside of a function's parameter's type + // - as parameter types are not instantiated from within a function's DC. We + // use isUnevaluatedContext() to distinguish the function parameter case. + CXXRecordDecl::LambdaDependencyKind DependencyKind = + CXXRecordDecl::LDK_Unknown; + if (getSema().isUnevaluatedContext() && + (getSema().CurContext->isFileContext() || + !getSema().CurContext->getParent()->isDependentContext())) + DependencyKind = CXXRecordDecl::LDK_NeverDependent; + CXXRecordDecl *OldClass = E->getLambdaClass(); - CXXRecordDecl *Class - = getSema().createLambdaClosureType(E->getIntroducerRange(), - NewCallOpTSI, - /*KnownDependent=*/false, - E->getCaptureDefault()); + CXXRecordDecl *Class = getSema().createLambdaClosureType( + E->getIntroducerRange(), NewCallOpTSI, + static_cast(DependencyKind), E->getCaptureDefault()); + getDerived().transformedLocalDecl(OldClass, {Class}); Optional> Mangling; diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1801,7 +1801,7 @@ using Capture = LambdaCapture; auto &Lambda = static_cast(Data); - Lambda.Dependent = Record.readInt(); + Lambda.DependencyKind = Record.readInt(); Lambda.IsGenericLambda = Record.readInt(); Lambda.CaptureDefault = Record.readInt(); Lambda.NumCaptures = Record.readInt(); @@ -1917,8 +1917,8 @@ // allocate the appropriate DefinitionData structure. bool IsLambda = Record.readInt(); if (IsLambda) - DD = new (C) CXXRecordDecl::LambdaDefinitionData(D, nullptr, false, false, - LCD_None); + DD = new (C) CXXRecordDecl::LambdaDefinitionData( + D, nullptr, CXXRecordDecl::LDK_Unknown, false, LCD_None); else DD = new (C) struct CXXRecordDecl::DefinitionData(D); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5726,7 +5726,7 @@ // Add lambda-specific data. if (Data.IsLambda) { auto &Lambda = D->getLambdaData(); - Record->push_back(Lambda.Dependent); + Record->push_back(Lambda.DependencyKind); Record->push_back(Lambda.IsGenericLambda); Record->push_back(Lambda.CaptureDefault); Record->push_back(Lambda.NumCaptures); 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 @@ -30,6 +30,27 @@ auto e = g(0); // expected-error{{no matching function for call}} // expected-note@-2 {{substitution failure}} +template +auto foo(decltype([] { + return [] { return T(); }(); +})) {} + +void test() { + foo({}); +} + +template +struct C { + template + auto foo(decltype([] { + return [] { return T(); }(); + })) {} +}; + +void test2() { + C{}.foo({}); +} + namespace PR52073 { // OK, these are distinct functions not redefinitions. template void f(decltype([]{})) {} // expected-note {{candidate}} @@ -40,6 +61,62 @@ template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} template void g(const char (*)[([]{ return N; })()]) {} // expected-note {{candidate}} // FIXME: We instantiate the lambdas into the context of the function template, -// so we think they're dependent and can't evaluate a call to them. +// so we think they're dependent and can't evaluate a call to them. void use_g() { g<6>(&"hello"); } // expected-error {{no matching function}} } + +namespace GH51416 { + +template +struct A { + void spam(decltype([] {})); +}; + +template +void A::spam(decltype([] {})) // expected-error{{out-of-line definition of 'spam' does not match}} +{} + +struct B { + template + void spam(decltype([] {})); +}; + +template +void B::spam(decltype([] {})) {} // expected-error{{out-of-line definition of 'spam' does not match}} + +} // namespace GH51416 + +namespace GH50376 { + +template +struct foo_t { // expected-note 2{{candidate constructor}} + foo_t(T ptr) {} // expected-note{{candidate constructor}} +}; + +template +using alias = foo_t; + +template +auto fun(T const &t) -> alias { + return alias{t}; // expected-error{{no viable conversion from returned value of type 'alias<...>'}} +} + +void f() { + int i; + auto const error = fun(i); // expected-note{{in instantiation}} +} + +} // namespace GH50376 + +namespace GH51414 { +template void spam(decltype([] {}) (*s)[sizeof(T)] = nullptr) {} +void foo() { + spam(); +} +} // namespace GH51414 + +namespace GH51641 { +template +void foo(decltype(+[](T) {}) lambda, T param); +static_assert(!__is_same(decltype(foo), void)); +} // namespace GH51641 diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -5824,6 +5824,7 @@ std::distance(FromL->decls().begin(), FromL->decls().end()); EXPECT_NE(ToLSize, 0u); EXPECT_EQ(ToLSize, FromLSize); + EXPECT_FALSE(FromL->isDependentLambda()); } TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionParam) { @@ -5843,6 +5844,7 @@ std::distance(FromL->decls().begin(), FromL->decls().end()); EXPECT_NE(ToLSize, 0u); EXPECT_EQ(ToLSize, FromLSize); + EXPECT_TRUE(FromL->isDependentLambda()); } TEST_P(ASTImporterOptionSpecificTestBase, LambdaInGlobalScope) { 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 @@ -1021,7 +1021,11 @@ Lambdas in unevaluated contexts P0315R4 - Partial + +
Partial + temp.deduct/9 is not implemented yet. +
+