Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (#108039)

The PR reapply https://github.com/llvm/llvm-project/pull/97308. 

- Implement [CWG1815](https://wg21.link/CWG1815): Support lifetime
extension of temporary created by aggregate initialization using a
default member initializer.

- Fix crash that introduced in
https://github.com/llvm/llvm-project/pull/97308. In
`InitListChecker::FillInEmptyInitForField`, when we enter
rebuild-default-init context, we copy all the contents of the parent
context to the current context, which will cause the `MaybeODRUseExprs`
to be lost. But we don't need to copy the entire context, only the
`DelayedDefaultInitializationContext` was required, which is used to
build `SourceLocExpr`, etc.

---------

Signed-off-by: yronglin <yronglin777@gmail.com>
This commit is contained in:
yronglin 2024-09-12 06:29:48 +08:00 committed by GitHub
parent c98d6c2e42
commit 060137038a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 277 additions and 87 deletions

View File

@ -108,6 +108,9 @@ C++ Language Changes
- Allow single element access of GCC vector/ext_vector_type object to be
constant expression. Supports the `V.xyzw` syntax and other tidbits
as seen in OpenCL. Selecting multiple elements is left as a future work.
- Implement `CWG1815 <https://wg21.link/CWG1815>`_. Support lifetime extension
of temporary created by aggregate initialization using a default member
initializer.
- Accept C++26 user-defined ``static_assert`` messages in C++11 as an extension.

View File

@ -10162,13 +10162,6 @@ def warn_dangling_pointer_assignment : Warning<
"will be destroyed at the end of the full-expression">,
InGroup<DanglingAssignment>;
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.
// Array comparisons have similar warnings

View File

@ -6403,6 +6403,9 @@ public:
/// example, in a for-range initializer).
bool InLifetimeExtendingContext = false;
/// Whether we should rebuild CXXDefaultArgExpr and CXXDefaultInitExpr.
bool RebuildDefaultArgOrDefaultInit = false;
// When evaluating immediate functions in the initializer of a default
// argument or default member initializer, this is the declaration whose
// default initializer is being evaluated and the location of the call
@ -7810,9 +7813,11 @@ public:
}
bool isInLifetimeExtendingContext() const {
assert(!ExprEvalContexts.empty() &&
"Must be in an expression evaluation context");
return ExprEvalContexts.back().InLifetimeExtendingContext;
return currentEvaluationContext().InLifetimeExtendingContext;
}
bool needsRebuildOfDefaultArgOrInit() const {
return currentEvaluationContext().RebuildDefaultArgOrDefaultInit;
}
bool isCheckingDefaultArgumentOrInitializer() const {
@ -7854,18 +7859,6 @@ public:
return Res;
}
/// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext
/// flag from previous context.
void keepInLifetimeExtendingContext() {
if (ExprEvalContexts.size() > 2 &&
parentEvaluationContext().InLifetimeExtendingContext) {
auto &LastRecord = ExprEvalContexts.back();
auto &PrevRecord = parentEvaluationContext();
LastRecord.InLifetimeExtendingContext =
PrevRecord.InLifetimeExtendingContext;
}
}
DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) {
return getDefaultedFunctionKind(FD).asComparison();
}

View File

@ -2509,8 +2509,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
// P2718R0 - Lifetime extension in range-based for loops.
if (getLangOpts().CPlusPlus23) {
auto &LastRecord = Actions.ExprEvalContexts.back();
auto &LastRecord = Actions.currentEvaluationContext();
LastRecord.InLifetimeExtendingContext = true;
LastRecord.RebuildDefaultArgOrDefaultInit = true;
}
if (getLangOpts().OpenMP)

View File

@ -896,11 +896,6 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
enum PathLifetimeKind {
/// Lifetime-extend along this path.
Extend,
/// We should lifetime-extend, but we don't because (due to technical
/// limitations) we can't. This happens for default member initializers,
/// which we don't clone for every use, so we don't have a unique
/// MaterializeTemporaryExpr to update.
ShouldExtend,
/// Do not lifetime extend along this path.
NoExtend
};
@ -912,7 +907,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
PathLifetimeKind Kind = PathLifetimeKind::Extend;
for (auto Elem : Path) {
if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
Kind = PathLifetimeKind::ShouldExtend;
return PathLifetimeKind::Extend;
else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
return PathLifetimeKind::NoExtend;
}
@ -1058,17 +1053,6 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
// 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.
SemaRef.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.

View File

@ -5429,6 +5429,8 @@ struct EnsureImmediateInvocationInDefaultArgs
EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef)
: TreeTransform(SemaRef) {}
bool AlwaysRebuild() { return true; }
// Lambda can only have immediate invocations in the default
// args of their parameters, which is transformed upon calling the closure.
// The body is not a subexpression, so we have nothing to do.
@ -5470,7 +5472,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
assert(Param->hasDefaultArg() && "can't build nonexistent default arg");
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
bool NeedRebuild = needsRebuildOfDefaultArgOrInit();
std::optional<ExpressionEvaluationContextRecord::InitializationContext>
InitializationContext =
OutermostDeclarationWithDelayedImmediateInvocations();
@ -5506,13 +5508,15 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
// Rewrite the call argument that was created from the corresponding
// parameter's default argument.
if (V.HasImmediateCalls || InLifetimeExtendingContext) {
if (V.HasImmediateCalls ||
(NeedRebuild && isa_and_present<ExprWithCleanups>(Param->getInit()))) {
if (V.HasImmediateCalls)
ExprEvalContexts.back().DelayedDefaultInitializationContext = {
CallLoc, Param, CurContext};
// Pass down lifetime extending flag, and collect temporaries in
// CreateMaterializeTemporaryExpr when we rewrite the call argument.
keepInLifetimeExtendingContext();
currentEvaluationContext().InLifetimeExtendingContext =
parentEvaluationContext().InLifetimeExtendingContext;
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
runWithSufficientStackSpace(CallLoc, [&] {
@ -5558,7 +5562,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
Expr *Init = nullptr;
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
bool NeedRebuild = needsRebuildOfDefaultArgOrInit();
EnterExpressionEvaluationContext EvalContext(
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
@ -5593,12 +5597,27 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
ImmediateCallVisitor V(getASTContext());
if (!NestedDefaultChecking)
V.TraverseDecl(Field);
if (V.HasImmediateCalls) {
// CWG1815
// Support lifetime extension of temporary created by aggregate
// initialization using a default member initializer. We should rebuild
// the initializer in a lifetime extension context if the initializer
// expression is an ExprWithCleanups. Then make sure the normal lifetime
// extension code recurses into the default initializer and does lifetime
// extension when warranted.
bool ContainsAnyTemporaries =
isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
if (Field->getInClassInitializer() &&
!Field->getInClassInitializer()->containsErrors() &&
(V.HasImmediateCalls || (NeedRebuild && ContainsAnyTemporaries))) {
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.
currentEvaluationContext().InLifetimeExtendingContext =
parentEvaluationContext().InLifetimeExtendingContext;
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
runWithSufficientStackSpace(Loc, [&] {
@ -17675,11 +17694,10 @@ void Sema::PopExpressionEvaluationContext() {
// Append the collected materialized temporaries into previous context before
// exit if the previous also is a lifetime extending context.
auto &PrevRecord = parentEvaluationContext();
if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext &&
PrevRecord.InLifetimeExtendingContext &&
parentEvaluationContext().InLifetimeExtendingContext &&
!Rec.ForRangeLifetimeExtendTemps.empty()) {
PrevRecord.ForRangeLifetimeExtendTemps.append(
parentEvaluationContext().ForRangeLifetimeExtendTemps.append(
Rec.ForRangeLifetimeExtendTemps);
}

View File

@ -1540,9 +1540,6 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
bool ListInitialization) {
QualType Ty = TInfo->getType();
SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc();
assert((!ListInitialization || Exprs.size() == 1) &&
"List initialization must have exactly one expression.");
SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc);
InitializedEntity Entity =

View File

@ -750,8 +750,21 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
if (Field->hasInClassInitializer()) {
if (VerifyOnly)
return;
ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
ExprResult DIE;
{
// Enter a default initializer rebuild context, then we can support
// lifetime extension of temporary created by aggregate initialization
// using a default member initializer.
// CWG1815 (https://wg21.link/CWG1815).
EnterExpressionEvaluationContext RebuildDefaultInit(
SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
true;
SemaRef.currentEvaluationContext().DelayedDefaultInitializationContext =
SemaRef.parentEvaluationContext()
.DelayedDefaultInitializationContext;
DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
}
if (DIE.isInvalid()) {
hadError = true;
return;
@ -7521,10 +7534,8 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary,
// are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary,
// but there may be a chance to merge them.
Cleanup.setExprNeedsCleanups(false);
if (isInLifetimeExtendingContext()) {
auto &Record = ExprEvalContexts.back();
Record.ForRangeLifetimeExtendTemps.push_back(MTE);
}
if (isInLifetimeExtendingContext())
currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE);
return MTE;
}

View File

@ -5481,7 +5481,10 @@ void Sema::InstantiateVariableInitializer(
EnterExpressionEvaluationContext Evaluated(
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var);
keepInLifetimeExtendingContext();
currentEvaluationContext().InLifetimeExtendingContext =
parentEvaluationContext().InLifetimeExtendingContext;
currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
parentEvaluationContext().RebuildDefaultArgOrDefaultInit;
// Instantiate the initializer.
ExprResult Init;

View File

@ -4254,7 +4254,10 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init,
getSema(), EnterExpressionEvaluationContext::InitList,
Construct->isListInitialization());
getSema().keepInLifetimeExtendingContext();
getSema().currentEvaluationContext().InLifetimeExtendingContext =
getSema().parentEvaluationContext().InLifetimeExtendingContext;
getSema().currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
getSema().parentEvaluationContext().RebuildDefaultArgOrDefaultInit;
SmallVector<Expr*, 8> NewArgs;
bool ArgChanged = false;
if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(),
@ -8924,8 +8927,9 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
// P2718R0 - Lifetime extension in range-based for loops.
if (getSema().getLangOpts().CPlusPlus23) {
auto &LastRecord = getSema().ExprEvalContexts.back();
auto &LastRecord = getSema().currentEvaluationContext();
LastRecord.InLifetimeExtendingContext = true;
LastRecord.RebuildDefaultArgOrDefaultInit = true;
}
StmtResult Init =
S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult();
@ -14443,6 +14447,13 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();
if (E->isListInitialization() && !E->isStdInitListInitialization()) {
ExprResult Res = RebuildInitList(E->getBeginLoc(), Args, E->getEndLoc());
if (Res.isInvalid())
return ExprError();
Args = {Res.get()};
}
}
if (!getDerived().AlwaysRebuild() &&
@ -14454,12 +14465,9 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
return SemaRef.MaybeBindToTemporary(E);
}
// FIXME: We should just pass E->isListInitialization(), but we're not
// prepared to handle list-initialization without a child InitListExpr.
SourceLocation LParenLoc = T->getTypeLoc().getEndLoc();
return getDerived().RebuildCXXTemporaryObjectExpr(
T, LParenLoc, Args, E->getEndLoc(),
/*ListInitialization=*/LParenLoc.isInvalid());
T, LParenLoc, Args, E->getEndLoc(), E->isListInitialization());
}
template<typename Derived>

View File

@ -789,10 +789,10 @@ void test() {
// CHECK-NEXT: "valueCategory": "lvalue",
// CHECK-NEXT: "extendingDecl": {
// CHECK-NEXT: "id": "0x{{.*}}",
// CHECK-NEXT: "kind": "FieldDecl",
// CHECK-NEXT: "name": "a",
// CHECK-NEXT: "kind": "VarDecl",
// CHECK-NEXT: "name": "b",
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "const A &"
// CHECK-NEXT: "qualType": "B"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "storageDuration": "automatic",

View File

@ -13,7 +13,7 @@ void test() {
}
// CHECK: -CXXDefaultInitExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue has rewritten init
// CHECK-NEXT: `-ExprWithCleanups 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Field 0x{{[^ ]*}} 'a' 'const A &'
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Var 0x{{[^ ]*}} 'b' 'B'
// CHECK-NEXT: `-ImplicitCastExpr 0x{{[^ ]*}} <{{.*}}> 'const A' <NoOp>
// CHECK-NEXT: `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A <NoOp>
// CHECK-NEXT: `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A'

View File

@ -120,10 +120,11 @@ void aggregateWithReferences() {
clang_analyzer_dump(viaReference); // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }}
clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }}
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`)
RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite`
// FIXME: clang currently support extending lifetime of object bound to reference members of aggregates,
// that are created from default member initializer. But CFG and ExprEngine need to be updated to address this change.
// The following expect warning: {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}
RefAggregate defaultInitExtended{i};
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
}

View File

@ -449,6 +449,27 @@ namespace cwg1696 { // cwg1696: 7
// since-cxx14-note@-2 {{default member initializer declared here}}
};
A a{a, a};
struct A1 {
A1() : v(42) {}
// since-cxx14-error@-1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
// since-cxx14-note@#cwg1696-A1 {{reference member declared here}}
const int &v; // #cwg1696-A1
};
struct A2 {
A2() = default;
// since-cxx14-error@-1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
// since-cxx14-note-re@#cwg1696-A2-b {{in defaulted default constructor for {{.*}} first required here}}
// since-cxx14-note@#cwg1696-A2-a {{initializing field 'v' with default member initializer}}
A2(int v) : v(v) {}
// since-cxx14-warning@-1 {{binding reference member 'v' to stack allocated parameter 'v'}}
// since-cxx14-note@#cwg1696-A2-a {{reference member declared here}}
const int &v = 42; // #cwg1696-A2-a
};
A2 a1; // #cwg1696-A2-b
A2 a2(1); // OK, unfortunately
#endif
}
@ -483,8 +504,6 @@ namespace cwg1696 { // cwg1696: 7
const A &a = A(); // #cwg1696-D1-a
};
D1 d1 = {}; // #cwg1696-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@#cwg1696-D1-a {{initializing field 'a' with default member initializer}}
struct D2 {
const A &a = A(); // #cwg1696-D2-a

View File

@ -206,19 +206,28 @@ namespace cwg1814 { // cwg1814: yes
#endif
}
namespace cwg1815 { // cwg1815: no
namespace cwg1815 { // cwg1815: 20
#if __cplusplus >= 201402L
// FIXME: needs codegen test
struct A { int &&r = 0; }; // #cwg1815-A
struct A { int &&r = 0; };
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@#cwg1815-A {{initializing field 'r' with default member initializer}}
struct B { int &&r = 0; }; // #cwg1815-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}}
// since-cxx14-note@#cwg1815-B {{initializing field 'r' with default member initializer}}
// since-cxx14-note@#cwg1815-b {{in implicit default constructor for 'cwg1815::B' first required here}}
B b; // #cwg1815-b
#if __cplusplus >= 201703L
struct C { const int &r = 0; };
constexpr C c = {}; // OK, since cwg1815
static_assert(c.r == 0);
constexpr int f() {
A a = {}; // OK, since cwg1815
return a.r;
}
static_assert(f() == 0);
#endif
#endif
}

View File

@ -269,6 +269,40 @@ 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() {}
};
B a = {};
// CHECK: call {{.*}}block_scope_begin_function
extern void block_scope_begin_function();
extern void block_scope_end_function();
block_scope_begin_function();
{
// CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
// CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
B b = {};
}
// CHECK: call {{.*}}block_scope_end_function
block_scope_end_function();
// CHECK: call {{.*}}some_other_function
extern void some_other_function();
some_other_function();
// CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
// CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
#endif
}
namespace P2718R0 {
namespace basic {
template <typename E> using T2 = std::list<E>;

View File

@ -0,0 +1,21 @@
// RUN: %clang_cc1 -o - -emit-llvm -triple x86_64-linux-gnu %s
// Check there are no crash issue CodeGen action.
// https://github.com/llvm/llvm-project/pull/97308
struct a {
} constexpr b;
class c {
public:
c(a);
};
class B {
public:
using d = int;
struct e {
enum { f } g;
int h;
c i;
d j{};
};
};
B::e k{B::e::f, int(), b};

View File

@ -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
}

View File

@ -27,6 +27,103 @@ class MemInit {
C m = s;
};
namespace std {
typedef decltype(sizeof(int)) size_t;
// libc++'s implementation
template <class _E> class initializer_list {
const _E *__begin_;
size_t __size_;
initializer_list(const _E *__b, size_t __s) : __begin_(__b), __size_(__s) {}
public:
typedef _E value_type;
typedef const _E &reference;
typedef const _E &const_reference;
typedef size_t size_type;
typedef const _E *iterator;
typedef const _E *const_iterator;
initializer_list() : __begin_(nullptr), __size_(0) {}
size_t size() const { return __size_; }
const _E *begin() const { return __begin_; }
const _E *end() const { return __begin_ + __size_; }
};
} // namespace std
#if __cplusplus >= 201703L
// Test CXXDefaultInitExpr rebuild issue in
// https://github.com/llvm/llvm-project/pull/87933
namespace test_rebuild {
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() {}
};
int TestBody_got;
template <int> class Vector {
public:
Vector(std::initializer_list<int>);
};
template <typename... Ts> Vector(Ts...) -> Vector<sizeof...(Ts)>;
class ProgramBuilder {
public:
template <typename T, typename ARGS> int *create(ARGS);
};
struct TypeTest : ProgramBuilder {
int *str_f16 = create<int>(Vector{0});
TypeTest() {}
};
class TypeTest_Element_Test : TypeTest {
void TestBody();
};
void TypeTest_Element_Test::TestBody() {
int *expect = str_f16;
&TestBody_got != expect; // expected-warning {{inequality comparison result unused}}
}
} // namespace test_rebuild
// Test CXXDefaultInitExpr rebuild issue in
// https://github.com/llvm/llvm-project/pull/92527
namespace test_rebuild2 {
struct F {
int g;
};
struct H {};
struct I {
I(const F &);
I(H);
};
struct L {
I i = I({.g = 0});
};
struct N : L {};
void f() {
delete new L; // Ok
delete new N; // Ok
}
} // namespace test_rebuild2
#endif // __cplusplus >= 201703L
#if __cplusplus >= 202002L
// This test ensures cleanup expressions are correctly produced
// in the presence of default member initializers.

View File

@ -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 {

View File

@ -10717,7 +10717,7 @@ and <I>POD class</I></td>
<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="unreleased" align="center">Clang 20</td>
</tr>
<tr id="1816">
<td><a href="https://cplusplus.github.io/CWG/issues/1816.html">1816</a></td>