Author: Haojian Wu Date: 2024-11-29T10:15:19+01:00 New Revision: 26baa00908b2eb8b2925800af6bc64fbe53cac48
URL: https://github.com/llvm/llvm-project/commit/26baa00908b2eb8b2925800af6bc64fbe53cac48 DIFF: https://github.com/llvm/llvm-project/commit/26baa00908b2eb8b2925800af6bc64fbe53cac48.diff LOG: [clang] Diagnose dangling references for parenthesized aggregate initialization. (#117690) Unlike brace initialization, the parenthesized aggregate initialization in C++20 does not extend the lifetime of a temporary object bound to a reference in an aggreate. This can lead to dangling references: ``` struct A { const int& r; }; A a1(1); // well-formed, but results in a dangling reference. ``` With this patch, clang will diagnose this common dangling issues. Fixes #101957 Added: clang/test/SemaCXX/cxx20-warn-dangling-paren-list-agg-init.cpp Modified: clang/docs/ReleaseNotes.rst clang/lib/Sema/CheckExprLifetime.cpp clang/test/AST/ByteCode/records.cpp clang/test/SemaCXX/paren-list-agg-init.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 102ffb56fec35f..9882a1c42d50c4 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -627,6 +627,8 @@ Improvements to Clang's diagnostics - Clang now supports using alias templates in deduction guides, aligning with the C++ standard, which treats alias templates as synonyms for their underlying types (#GH54909). +- Clang now diagnoses dangling references for C++20's parenthesized aggregate initialization (#101957). + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index de69d6537b5692..15ceff873693a5 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -204,6 +204,7 @@ struct IndirectLocalPathEntry { GslPointerInit, GslPointerAssignment, DefaultArg, + ParenAggInit, } Kind; Expr *E; union { @@ -985,6 +986,17 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init)) return visitFunctionCallArguments(Path, Init, Visit); + if (auto *CPE = dyn_cast<CXXParenListInitExpr>(Init)) { + RevertToOldSizeRAII RAII(Path); + Path.push_back({IndirectLocalPathEntry::ParenAggInit, CPE}); + for (auto *I : CPE->getInitExprs()) { + if (I->isGLValue()) + visitLocalsRetainedByReferenceBinding(Path, I, RK_ReferenceBinding, + Visit); + else + visitLocalsRetainedByInitializer(Path, I, Visit, true); + } + } switch (Init->getStmtClass()) { case Stmt::UnaryOperatorClass: { auto *UO = cast<UnaryOperator>(Init); @@ -1081,6 +1093,7 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I, case IndirectLocalPathEntry::GslReferenceInit: case IndirectLocalPathEntry::GslPointerInit: case IndirectLocalPathEntry::GslPointerAssignment: + case IndirectLocalPathEntry::ParenAggInit: // These exist primarily to mark the path as not permitting or // supporting lifetime extension. break; @@ -1392,6 +1405,7 @@ checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity, switch (Elem.Kind) { case IndirectLocalPathEntry::AddressOf: case IndirectLocalPathEntry::LValToRVal: + case IndirectLocalPathEntry::ParenAggInit: // These exist primarily to mark the path as not permitting or // supporting lifetime extension. break; diff --git a/clang/test/AST/ByteCode/records.cpp b/clang/test/AST/ByteCode/records.cpp index 2eeaafc04c516c..4601aface135ee 100644 --- a/clang/test/AST/ByteCode/records.cpp +++ b/clang/test/AST/ByteCode/records.cpp @@ -1015,11 +1015,13 @@ namespace ParenInit { }; /// Not constexpr! - O o1(0); + O o1(0); // both-warning {{temporary whose address is used as value of}} + // FIXME: the secondary warning message is bogus, would be nice to suppress it. constinit O o2(0); // both-error {{variable does not have a constant initializer}} \ // both-note {{required by 'constinit' specifier}} \ // both-note {{reference to temporary is not a constant expression}} \ - // both-note {{temporary created here}} + // both-note {{temporary created here}} \ + // both-warning {{temporary whose address is used as value}} /// Initializing an array. diff --git a/clang/test/SemaCXX/cxx20-warn-dangling-paren-list-agg-init.cpp b/clang/test/SemaCXX/cxx20-warn-dangling-paren-list-agg-init.cpp new file mode 100644 index 00000000000000..645a7caba714e2 --- /dev/null +++ b/clang/test/SemaCXX/cxx20-warn-dangling-paren-list-agg-init.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -verify -std=c++20 %s -fsyntax-only + +namespace std { +template <class T> struct remove_reference { typedef T type; }; +template <class T> struct remove_reference<T&> { typedef T type; }; +template <class T> struct remove_reference<T&&> { typedef T type; }; + +template <class T> typename remove_reference<T>::type &&move(T &&t); +} // namespace std + +// dcl.init 16.6.2.2 +struct A { + int a; + int&& r; +}; + +int f(); +int n = 10; + +A a1{1, f()}; // OK, lifetime is extended for direct-list-initialization +// well-formed, but dangling reference +A a2(1, f()); // expected-warning {{temporary whose address is used as value}} +// well-formed, but dangling reference +A a4(1.0, 1); // expected-warning {{temporary whose address is used as value}} +A a5(1.0, std::move(n)); // OK + + + +struct B { + const int& r; +}; +B test(int local) { + return B(1); // expected-warning {{returning address}} + return B(local); // expected-warning {{address of stack memory}} +} + +void f(B b); +void test2(int local) { + // No diagnostic on the following cases where both the aggregate object and + // temporary end at the end of the full expression. + f(B(1)); + f(B(local)); +} + +// Test nested struct. +struct C { + B b; +}; + +struct D { + C c; +}; + +C c1(B( + 1 // expected-warning {{temporary whose address is used as value}} +)); +D d1(C(B( + 1 // expected-warning {{temporary whose address is used as value}} +))); diff --git a/clang/test/SemaCXX/paren-list-agg-init.cpp b/clang/test/SemaCXX/paren-list-agg-init.cpp index cc2a9d88dd4a6e..61afba85e1dff9 100644 --- a/clang/test/SemaCXX/paren-list-agg-init.cpp +++ b/clang/test/SemaCXX/paren-list-agg-init.cpp @@ -116,6 +116,7 @@ void foo(int n) { // expected-note {{declared here}} B b2(A(1), {}, 1); // beforecxx20-warning@-1 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}} // beforecxx20-warning@-2 {{aggregate initialization of type 'B' from a parenthesized list of values is a C++20 extension}} + // expected-warning@-3 {{temporary whose address is used as value of local variable 'b2' will be destroyed at the end of the full-expression}} C c(A(1), 1, 2, 3, 4); // expected-error@-1 {{array initializer must be an initializer list}} @@ -262,9 +263,12 @@ struct O { O o1(0, 0, 0); // no-error // beforecxx20-warning@-1 {{aggregate initialization of type 'O' from a parenthesized list of values is a C++20 extension}} +// expected-warning@-2 {{temporary whose address is used as value of local variable 'o1' will be destroyed at the end of the full-expression}} +// expected-warning@-3 {{temporary whose address is used as value of local variable 'o1' will be destroyed at the end of the full-expression}} O o2(0, 0); // no-error // beforecxx20-warning@-1 {{aggregate initialization of type 'O' from a parenthesized list of values is a C++20 extension}} +// expected-warning@-2 {{temporary whose address is used as value of local variable 'o2' will be destroyed at the end of the full-expression}} O o3(0); // expected-error@-1 {{reference member of type 'int &&' uninitialized}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits