diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -83,6 +83,14 @@ per identifier. Fixes `Issue 28985 `_. +- 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ``-Wliteral-range`` will warn on floating-point equality comparisons with 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,7 +555,7 @@ 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); static CXXRecordDecl *CreateDeserialized(const ASTContext &C, unsigned ID); @@ -1774,7 +1782,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 @@ -6783,7 +6783,7 @@ /// Create a new lambda closure type. CXXRecordDecl *createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info, - bool KnownDependent, + unsigned LambdaDependencyKind, LambdaCaptureDefault CaptureDefault); /// Start the definition of a lambda expression. 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 @@ -2874,7 +2874,7 @@ return TInfoOrErr.takeError(); if (GetImportedOrCreateSpecialDecl( D2CXX, CXXRecordDecl::CreateLambda, D, Importer.getToContext(), - DC, *TInfoOrErr, Loc, DCXX->isDependentLambda(), + DC, *TInfoOrErr, Loc, DCXX->getLambdaDependencyKind(), DCXX->isGenericLambda(), DCXX->getLambdaCaptureDefault())) return D2CXX; ExpectedDecl CDeclOrErr = import(DCXX->getLambdaContextDecl()); 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,16 +146,16 @@ 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; } 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,21 +238,19 @@ 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; @@ -898,17 +896,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 +976,8 @@ UPPC_DeclarationType); } - CXXRecordDecl *Class = createLambdaClosureType(Intro.Range, MethodTyInfo, - KnownDependent, Intro.Default); + CXXRecordDecl *Class = createLambdaClosureType( + Intro.Range, MethodTyInfo, 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 @@ -1853,7 +1853,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 @@ -12982,14 +12982,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, + 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 @@ -5733,7 +5733,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 @@ -1024,7 +1024,11 @@ Lambdas in unevaluated contexts P0315R4 - Partial + +
Partial + temp.deduct/9 is not implemented yet. +
+