-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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
[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer #87933
Conversation
…initialization using a default member initializer Signed-off-by: yronglin <yronglin777@gmail.com>
@llvm/pr-subscribers-clang Author: None (yronglin) ChangesThis PR complete DR1815 under the guidance of Full diff: https://github.com/llvm/llvm-project/pull/87933.diff 9 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a1dda2d2461c31..ba779e83d2afd4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10010,12 +10010,6 @@ def warn_new_dangling_initializer_list : Warning<
"the allocated initializer list}0 "
"will be destroyed at the end of the full-expression">,
InGroup<DanglingInitializerList>;
-def warn_unsupported_lifetime_extension : Warning<
- "lifetime extension of "
- "%select{temporary|backing array of initializer list}0 created "
- "by aggregate initialization using a default member initializer "
- "is not yet supported; lifetime of %select{temporary|backing array}0 "
- "will end at the end of the full-expression">, InGroup<Dangling>;
// For non-floating point, expressions of the form x == x or x != x
// should result in a warning, since these always evaluate to a constant.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 8db4fffeecfe35..b2e0f2a2a60113 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6338,10 +6338,9 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
Res = Immediate.TransformInitializer(Param->getInit(),
/*NotCopy=*/false);
});
- if (Res.isInvalid())
- return ExprError();
- Res = ConvertParamDefaultArgument(Param, Res.get(),
- Res.get()->getBeginLoc());
+ if (Res.isUsable())
+ Res = ConvertParamDefaultArgument(Param, Res.get(),
+ Res.get()->getBeginLoc());
if (Res.isInvalid())
return ExprError();
Init = Res.get();
@@ -6377,7 +6376,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
Expr *Init = nullptr;
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+ bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
EnterExpressionEvaluationContext EvalContext(
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
@@ -6412,11 +6411,14 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
ImmediateCallVisitor V(getASTContext());
if (!NestedDefaultChecking)
V.TraverseDecl(Field);
- if (V.HasImmediateCalls) {
+ if (V.HasImmediateCalls || InLifetimeExtendingContext) {
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
CurContext};
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
NestedDefaultChecking;
+ // Pass down lifetime extending flag, and collect temporaries in
+ // CreateMaterializeTemporaryExpr when we rewrite the call argument.
+ keepInLifetimeExtendingContext();
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
@@ -6424,7 +6426,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
/*CXXDirectInit=*/false);
});
- if (!Res.isInvalid())
+ if (Res.isUsable())
Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
if (Res.isInvalid()) {
Field->setInvalidDecl();
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index a75e9925a43146..842412cd674d8c 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -710,6 +710,26 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
if (VerifyOnly)
return;
+ // Enter a lifetime extension context, then we can support lifetime
+ // extension of temporary created by aggregate initialization using a
+ // default member initializer (DR1815 https://wg21.link/CWG1815).
+ //
+ // In a lifetime extension context, BuildCXXDefaultInitExpr will clone the
+ // initializer expression on each use that would lifetime extend its
+ // temporaries.
+ EnterExpressionEvaluationContext LifetimeExtensionContext(
+ SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+ /*LambdaContextDecl=*/nullptr,
+ Sema::ExpressionEvaluationContextRecord::EK_Other, true);
+
+ // Lifetime extension in default-member-init.
+ auto &LastRecord = SemaRef.ExprEvalContexts.back();
+
+ // Just copy previous record, make sure we haven't forget anything.
+ LastRecord =
+ SemaRef.ExprEvalContexts[SemaRef.ExprEvalContexts.size() - 2];
+ LastRecord.InLifetimeExtendingContext = true;
+
ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
if (DIE.isInvalid()) {
hadError = true;
@@ -7699,6 +7719,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
// Step into CXXDefaultInitExprs so we can diagnose cases where a
// constructor inherits one as an implicit mem-initializer.
if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) {
+ assert(DIE->hasRewrittenInit() &&
+ "CXXDefaultInitExpr must has rewritten init");
Path.push_back(
{IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});
Init = DIE->getExpr();
@@ -8193,6 +8215,11 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
}
switch (shouldLifetimeExtendThroughPath(Path)) {
+ case PathLifetimeKind::ShouldExtend:
+ // We're supposed to lifetime-extend the temporary along this path (per
+ // the resolution of DR1815), we supported that by clone the initializer
+ // expression on each use that would lifetime extend its temporaries.
+ [[fallthrough]];
case PathLifetimeKind::Extend:
// Update the storage duration of the materialized temporary.
// FIXME: Rebuild the expression instead of mutating it.
@@ -8200,18 +8227,6 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
ExtendingEntity->allocateManglingNumber());
// Also visit the temporaries lifetime-extended by this initializer.
return true;
-
- case PathLifetimeKind::ShouldExtend:
- // We're supposed to lifetime-extend the temporary along this path (per
- // the resolution of DR1815), but we don't support that yet.
- //
- // FIXME: Properly handle this situation. Perhaps the easiest approach
- // would be to clone the initializer expression on each use that would
- // lifetime extend its temporaries.
- Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
- << RK << DiagRange;
- break;
-
case PathLifetimeKind::NoExtend:
// If the path goes through the initialization of a variable or field,
// it can't possibly reach a temporary created in this full-expression.
diff --git a/clang/test/Analysis/lifetime-extended-regions.cpp b/clang/test/Analysis/lifetime-extended-regions.cpp
index 4e98bd4b0403eb..8df9b8a54a7e02 100644
--- a/clang/test/Analysis/lifetime-extended-regions.cpp
+++ b/clang/test/Analysis/lifetime-extended-regions.cpp
@@ -122,7 +122,7 @@ void aggregateWithReferences() {
clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }}
// clang does not currently implement extending lifetime of object bound to reference members of aggregates,
- // that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`)
+ // that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`) (Supported now).
RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite`
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
}
diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp
index f4d6c04fb8e073..52002a7d0db002 100644
--- a/clang/test/CXX/drs/dr16xx.cpp
+++ b/clang/test/CXX/drs/dr16xx.cpp
@@ -484,8 +484,6 @@ namespace dr1696 { // dr1696: 7
const A &a = A(); // #dr1696-D1-a
};
D1 d1 = {}; // #dr1696-d1
- // since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}}
- // since-cxx14-note@#dr1696-D1-a {{initializing field 'a' with default member initializer}}
struct D2 {
const A &a = A(); // #dr1696-D2-a
diff --git a/clang/test/CXX/drs/dr18xx.cpp b/clang/test/CXX/drs/dr18xx.cpp
index e78730e8992cf8..52a90ea3a05d26 100644
--- a/clang/test/CXX/drs/dr18xx.cpp
+++ b/clang/test/CXX/drs/dr18xx.cpp
@@ -206,13 +206,10 @@ namespace dr1814 { // dr1814: yes
#endif
}
-namespace dr1815 { // dr1815: no
+namespace dr1815 { // dr1815: yes
#if __cplusplus >= 201402L
- // FIXME: needs codegen test
struct A { int &&r = 0; }; // #dr1815-A
A a = {};
- // since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}} FIXME
- // since-cxx14-note@#dr1815-A {{initializing field 'r' with default member initializer}}
struct B { int &&r = 0; }; // #dr1815-B
// since-cxx14-error@-1 {{reference member 'r' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp
index 5554363cc69abb..002bf3e1c26740 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -269,6 +269,26 @@ void init_capture_init_list() {
// CHECK: }
}
+void check_dr1815() { // dr1815: yes
+#if __cplusplus >= 201402L
+
+ struct A {
+ int &&r = 0;
+ ~A() {}
+ };
+
+ struct B {
+ A &&a = A{};
+ ~B() {}
+ };
+
+ // CHECK: void @_Z12check_dr1815v()
+ // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev(
+ // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev(
+ B a = {};
+#endif
+}
+
namespace P2718R0 {
namespace basic {
template <typename E> using T2 = std::list<E>;
diff --git a/clang/test/SemaCXX/constexpr-default-arg.cpp b/clang/test/SemaCXX/constexpr-default-arg.cpp
index 7c883692829548..8510a6ddc80399 100644
--- a/clang/test/SemaCXX/constexpr-default-arg.cpp
+++ b/clang/test/SemaCXX/constexpr-default-arg.cpp
@@ -32,8 +32,8 @@ void test_default_arg2() {
}
// Check that multiple CXXDefaultInitExprs don't cause an assertion failure.
-struct A { int &&r = 0; }; // expected-note 2{{default member initializer}}
+struct A { int &&r = 0; };
struct B { A x, y; };
-B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}}
+B b = {}; // expected-no-diagnostics
}
diff --git a/clang/test/SemaCXX/eval-crashes.cpp b/clang/test/SemaCXX/eval-crashes.cpp
index 017df977b26b7b..a06f60f71e9c7e 100644
--- a/clang/test/SemaCXX/eval-crashes.cpp
+++ b/clang/test/SemaCXX/eval-crashes.cpp
@@ -25,11 +25,9 @@ namespace pr33140_0b {
}
namespace pr33140_2 {
- // FIXME: The declaration of 'b' below should lifetime-extend two int
- // temporaries.
- struct A { int &&r = 0; }; // expected-note 2{{initializing field 'r' with default member initializer}}
+ struct A { int &&r = 0; };
struct B { A x, y; };
- B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}}
+ B b = {};
}
namespace pr33140_3 {
|
friendly ping~ |
Signed-off-by: yronglin <yronglin777@gmail.com>
friendly ping~ |
@hubert-reinterpretcast ping! |
Friendly ping! @zygoloid @hubert-reinterpretcast |
Signed-off-by: yronglin <yronglin777@gmail.com>
friendly ping~ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this.
// clang does not currently implement extending lifetime of object bound to reference members of aggregates, | ||
// that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`) | ||
// that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`) (Supported now). | ||
RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite` | ||
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the bug is fixed, does the analyzer output not change here? In any case, the comment should explain whatever the new behavior is, and shouldn't be mentioning a warning that no longer exists.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, good catch! we need update this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please give me some time to investigate, I'm not very familiar with analyzer.
clang/www/cxx_dr_status.html
Outdated
@@ -10698,7 +10698,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> | |||
<td><a href="https://cplusplus.github.io/CWG/issues/1815.html">1815</a></td> | |||
<td>CD4</td> | |||
<td>Lifetime extension in aggregate initialization</td> | |||
<td class="none" align="center">No</td> | |||
<td class="full" align="center">Clang 19</td> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<td class="full" align="center">Clang 19</td> | |
<td class="unreleased" align="center">Clang 19</td> |
We use a different CSS class here for features that haven't been in an official release yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your tips!
Thanks for you review! |
Signed-off-by: yronglin <yronglin777@gmail.com>
@ZequanWu thank you for your help, but next time you should disable renaming passes as specified in #89807 (comment). C-Reduce output can be typically reduced further manually, but it's a pain without names. |
Thanks, I'll take a look now. |
This change allows us to pass creduce options to creduce-clang-crash.py script. With this, `--n` is no longer needed to specify the number of cores, so removed the flag. The motivation is #87933 (comment) suggests that disabling creduce renaming passes helps people to further reduce crash manually.
I've a more short reproducer: namespace std {
template <class T> class initializer_list {};
}
template <typename T, int> class C {
public:
C(std::initializer_list<T>);
};
template <typename T> using Ptr =__remove_pointer(T) *;
template <typename T> C(T) -> C<Ptr<T>, sizeof(T)>;
class A {
public:
template <typename T1, typename T2>
T1 *some_func(T2 &&);
};
struct B : A {
int *ar = some_func<int>(C{some_func<int>(0)});
B() {}
};
|
Great, I just finished a creduce run with renaming pass disabled, but yours is short. |
I've debug in local. The crash issue caused by initializer rebuild failed in llvm-project/clang/lib/Sema/SemaExpr.cpp Lines 5717 to 5721 in 8e00703
SFINAETrap Trap(*this); , we can got diagnostics like the following:
But why does the check pass during Parse phase but fails when we rebuilding llvm-project/clang/lib/Sema/TreeTransform.h Lines 14116 to 14121 in 8e00703
Therefore, I think the key to the problem is to fix |
Can we revert this change while you are investigating if this fix is not trivial? |
) This patch covers the following Core issues: [CWG930](https://cplusplus.github.io/CWG/issues/930.html) "`alignof` with incomplete array type" [CWG1110](https://cplusplus.github.io/CWG/issues/1110.html) "Incomplete return type should be allowed in `decltype` operand" [CWG1340](https://cplusplus.github.io/CWG/issues/1340.html) "Complete type in member pointer expressions" [CWG1352](https://cplusplus.github.io/CWG/issues/1352.html) "Inconsistent class scope and completeness rules" [CWG1458](https://cplusplus.github.io/CWG/issues/1458.html) "Address of incomplete type vs `operator&()`" [CWG1824](https://cplusplus.github.io/CWG/issues/1824.html) "Completeness of return type vs point of instantiation" [CWG1832](https://cplusplus.github.io/CWG/issues/1832.html) "Casting to incomplete enumeration" [CWG2304](https://cplusplus.github.io/CWG/issues/2304.html) "Incomplete type vs overload resolution" [CWG2310](https://cplusplus.github.io/CWG/issues/2310.html) "Type completeness and derived-to-base pointer conversions" [CWG2430](https://cplusplus.github.io/CWG/issues/2430.html) "Completeness of return and parameter types of member functions" [CWG2512](https://cplusplus.github.io/CWG/issues/2512.html) "`typeid` and incomplete class types" [CWG2630](https://cplusplus.github.io/CWG/issues/2630.html) "Syntactic specification of class completeness" [CWG2718](https://cplusplus.github.io/CWG/issues/2718.html) "Type completeness for derived-to-base conversions" [CWG2857](https://cplusplus.github.io/CWG/issues/2857.html) "Argument-dependent lookup with incomplete class types" Current wording for CWG1110 came from [P0135R1](https://wg21.link/p0135R1) "Wording for guaranteed copy elision through simplified value categories". As a drive-by fix, I fixed incorrect status of CWG1815, test for which was added in #87933. CC @yronglin
Here's a smaller repro of the
Can you verify if this is a false positive or not? If it's a false positive and would take a while to fix, can you revert this commit? Update:
|
Sorry for the late reply, I found the update of comments from the email, and I have a new PR that fixes these problems. |
@erichkeane Can you please take a look? |
Here's a test case that clang diagnoses with I'm having a hard time telling whether this code is ill-defined NDR, or if the compiler is required to accept this. Clang stopped diagnosing this code with the fix for CWG2631, ca61961. Now this PR makes it diagnose again, despite that the default arg is never actually used.
|
I don't think this clause of the standard is violated: you need to instantiate a declaration of the conversion function to at the end of definition of |
We don't diagnose it at the end of the definition of M -- it looks like we explicitly intentionally stopped doing so in the commit I referenced. That's why I'm a little confused here. With this patch, we start to diagnose only in the final line of code, |
Thanks for the new test case, it's fixed with #92527 |
Looking now, I was out on Friday due to illness. |
I'm sorry to hear that. Sending all my best and wishing you a rapid comeback. |
This PR complete DR1815 under the guidance of
FIXME
comments. And reuseCXXDefaultInitExpr
rewrite machinery to clone the initializer expression on each use that would lifetime extend its temporaries.