mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 09:06:43 +00:00
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:
parent
c98d6c2e42
commit
060137038a
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
@ -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'
|
||||
|
@ -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 }}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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>;
|
||||
|
21
clang/test/SemaCXX/PR97308.cpp
Normal file
21
clang/test/SemaCXX/PR97308.cpp
Normal 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};
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user