[clang] [clang][Sema] Separate aggregate default member initializer evaluation (PR #219288)
Eli Friedman via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 10 18:00:49 PDT 2026
================
@@ -5933,80 +5939,136 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
// 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() &&
+ bool ContainsAnyTemporaries = isa<ExprWithCleanups>(InClassInit);
+ Expr *Init = InClassInit;
+ if (!InClassInit->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.
+ // CreateMaterializeTemporaryExpr when we rewrite the initializer.
currentEvaluationContext().InLifetimeExtendingContext =
parentEvaluationContext().InLifetimeExtendingContext;
+
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
runWithSufficientStackSpace(Loc, [&] {
- Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
+ Res = Immediate.TransformInitializer(InClassInit,
/*CXXDirectInit=*/false);
});
if (!Res.isInvalid())
- Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
+ Res = ConvertMemberDefaultInitExpression(Field, Entity, Res.get(), Loc);
if (Res.isInvalid()) {
Field->setInvalidDecl();
return ExprError();
}
Init = Res.get();
}
- if (Field->getInClassInitializer()) {
- Expr *E = Init ? Init : Field->getInClassInitializer();
- if (!NestedDefaultChecking)
- runWithSufficientStackSpace(Loc, [&] {
- MarkDeclarationsReferencedInExpr(E, /*SkipLocalVariables=*/false);
- });
- if (isInLifetimeExtendingContext())
- DiscardCleanupsInEvaluationContext();
- // C++11 [class.base.init]p7:
- // The initialization of each base and member constitutes a
- // full-expression.
- ExprResult Res = ActOnFinishFullExpr(E, /*DiscardedValue=*/false);
- if (Res.isInvalid()) {
- Field->setInvalidDecl();
- return ExprError();
- }
- Init = Res.get();
+ if (!NestedDefaultChecking)
+ runWithSufficientStackSpace(Loc, [&] {
+ MarkDeclarationsReferencedInExpr(Init, /*SkipLocalVariables=*/false);
+ });
+ return Init;
+}
- return CXXDefaultInitExpr::Create(Context, InitializationContext->Loc,
- Field, InitializationContext->Context,
- Init);
- }
+ExprResult Sema::BuildCXXCtorDefaultInitExpr(SourceLocation Loc,
+ FieldDecl *Field) {
+ assert(Field->hasInClassInitializer());
- // DR1351:
- // If the brace-or-equal-initializer of a non-static data member
- // invokes a defaulted default constructor of its class or of an
- // enclosing class in a potentially evaluated subexpression, the
- // program is ill-formed.
- //
- // This resolution is unworkable: the exception specification of the
- // default constructor can be needed in an unevaluated context, in
- // particular, in the operand of a noexcept-expression, and we can be
- // unable to compute an exception specification for an enclosed class.
+ bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
+
+ // C++11 [class.base.init]p7:
+ // The initialization of each base and member constitutes a
+ // full-expression.
+ // So this initializer gets an evaluation context of its own, and is finished
+ // as a full-expression below.
+ EnterExpressionEvaluationContext EvalContext(
+ *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
+ CXXThisScopeRAII This(*this, Field->getParent(), Qualifiers());
+
+ auto InitContext = OutermostDeclarationWithDelayedImmediateInvocations();
+ if (!InitContext)
+ InitContext.emplace(Loc, Field, CurContext);
+
+ // [class.temporary]/p7:
+ // If such a temporary object would otherwise be destroyed at the end of the
+ // for-range-initializer full-expression, the object persists for the lifetime
+ // of the reference initialized by the for-range-initializer.
//
- // Any attempt to resolve the exception specification of a defaulted default
- // constructor before the initializer is lexically complete will ultimately
- // come here at which point we can diagnose it.
- RecordDecl *OutermostClass = ParentRD->getOuterLexicalRecordContext();
- Diag(Loc, diag::err_default_member_initializer_not_yet_parsed)
- << OutermostClass << Field;
- Diag(Field->getEndLoc(),
- diag::note_default_member_initializer_not_yet_parsed);
- // Recover by marking the field invalid, unless we're in a SFINAE context.
- if (!isSFINAEContext())
+ // A default member initializer used by a constructor is a separate
+ // full-expression, we don't need extend temporaries lifetime in this
+ // situation, the NeedRebuild will always false.
+ ExprResult Init = BuildCXXDefaultInitInternal(
+ Loc, Field,
+ InitializedEntity::InitializeMemberFromDefaultMemberInitializer(Field),
+ NestedDefaultChecking, /*NeedRebuild=*/false);
+ if (Init.isInvalid())
+ return ExprError();
+
+ if (isInLifetimeExtendingContext())
----------------
efriedma-quic wrote:
InLifetimeExtendingContext can't be true here, I think?
https://github.com/llvm/llvm-project/pull/219288
More information about the cfe-commits
mailing list