Skip to content

[clang] Diagnose dangling references for parenthesized aggregate initialization. #117690

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 2 commits into from
Nov 29, 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
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
----------------------------------

Expand Down
14 changes: 14 additions & 0 deletions clang/lib/Sema/CheckExprLifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ struct IndirectLocalPathEntry {
GslPointerInit,
GslPointerAssignment,
DefaultArg,
ParenAggInit,
} Kind;
Expr *E;
union {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 4 additions & 2 deletions clang/test/AST/ByteCode/records.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
59 changes: 59 additions & 0 deletions clang/test/SemaCXX/cxx20-warn-dangling-paren-list-agg-init.cpp
Original file line number Diff line number Diff line change
@@ -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}}
)));
4 changes: 4 additions & 0 deletions clang/test/SemaCXX/paren-list-agg-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down Expand Up @@ -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}}
Expand Down