[clang] [Clang] Implement P2718R0 "Lifetime extension in range-based for loops" (PR #76361)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 27 07:26:19 PST 2024
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/76361
>From 20b860d86e1db5981b6e362a11ac3ac437a4f007 Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sat, 27 Jan 2024 23:25:57 +0800
Subject: [PATCH] [Clang] Implement P2718R0 "Lifetime extension in range-based
for loops"
Implement P2718R0 "Lifetime extension in range-based for loops" (https://wg21.link/P2718R0)
Differential Revision: https://reviews.llvm.org/D153701
---
clang/docs/ReleaseNotes.rst | 3 +
clang/include/clang/Parse/Parser.h | 2 +-
clang/include/clang/Sema/Sema.h | 104 ++++-
clang/lib/Frontend/InitPreprocessor.cpp | 4 +-
clang/lib/Parse/ParseDecl.cpp | 26 ++
clang/lib/Parse/ParseStmt.cpp | 8 +-
clang/lib/Sema/SemaExpr.cpp | 34 +-
clang/lib/Sema/SemaExprCXX.cpp | 46 ++-
clang/lib/Sema/SemaInit.cpp | 4 +
clang/lib/Sema/SemaStmt.cpp | 35 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 +
clang/lib/Sema/TreeTransform.h | 62 +--
.../test/AST/ast-dump-for-range-lifetime.cpp | 355 ++++++++++++++++++
clang/test/CXX/special/class.temporary/p6.cpp | 311 ++++++++++++++-
clang/test/Lexer/cxx-features.cpp | 2 +-
clang/www/cxx_status.html | 2 +-
16 files changed, 913 insertions(+), 87 deletions(-)
create mode 100644 clang/test/AST/ast-dump-for-range-lifetime.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5330cd9caad8011..2bbbf2b76f87e08 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -65,6 +65,9 @@ C++20 Feature Support
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
+- Implemented `P2718R0: Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_. Also
+ materialize temporary object which is a prvalue in discarded-value expression.
+
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index bde1ac2242cd6c2..d00fc0535e7eb09 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2398,7 +2398,7 @@ class Parser : public CodeCompletionHandler {
struct ForRangeInit {
SourceLocation ColonLoc;
ExprResult RangeExpr;
-
+ SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
};
struct ForRangeInfo : ForRangeInit {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1f1cbd11ff73581..00bf78f46c11d85 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1342,6 +1342,12 @@ class Sema final {
/// context not already known to be immediately invoked.
llvm::SmallPtrSet<DeclRefExpr *, 4> ReferenceToConsteval;
+ /// P2718R0 - Lifetime extension in range-based for loops.
+ /// MaterializeTemporaryExprs in for-range-init expressions which need to
+ /// extend lifetime. Add MaterializeTemporaryExpr* if the value of
+ /// InLifetimeExtendingContext is true.
+ SmallVector<MaterializeTemporaryExpr *, 8> ForRangeLifetimeExtendTemps;
+
/// \brief Describes whether we are in an expression constext which we have
/// to handle differently.
enum ExpressionKind {
@@ -1361,6 +1367,39 @@ class Sema final {
// VLAs).
bool InConditionallyConstantEvaluateContext = false;
+ /// Whether we are currently in a context in which all temporaries must be
+ /// lifetime-extended, even if they're not bound to a reference (for
+ /// example, in a for-range initializer).
+ bool InLifetimeExtendingContext = false;
+
+ /// Whether we are currently in a context in which all temporaries must be
+ /// materialized.
+ ///
+ /// [class.temporary]/p2:
+ /// The materialization of a temporary object is generally delayed as long
+ /// as possible in order to avoid creating unnecessary temporary objects.
+ ///
+ /// Temporary objects are materialized:
+ /// (2.1) when binding a reference to a prvalue ([dcl.init.ref],
+ /// [expr.type.conv], [expr.dynamic.cast], [expr.static.cast],
+ /// [expr.const.cast], [expr.cast]),
+ ///
+ /// (2.2) when performing member access on a class prvalue ([expr.ref],
+ /// [expr.mptr.oper]),
+ ///
+ /// (2.3) when performing an array-to-pointer conversion or subscripting
+ /// on an array prvalue ([conv.array], [expr.sub]),
+ ///
+ /// (2.4) when initializing an object of type
+ /// std::initializer_list<T> from a braced-init-list
+ /// ([dcl.init.list]),
+ ///
+ /// (2.5) for certain unevaluated operands ([expr.typeid], [expr.sizeof])
+ ///
+ /// (2.6) when a prvalue that has type other than cv void appears as a
+ /// discarded-value expression ([expr.context]).
+ bool InMaterializeTemporaryObjectContext = 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
@@ -5245,22 +5284,17 @@ class Sema final {
BFRK_Check
};
- StmtResult ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
- SourceLocation CoawaitLoc,
- Stmt *InitStmt,
- Stmt *LoopVar,
- SourceLocation ColonLoc, Expr *Collection,
- SourceLocation RParenLoc,
- BuildForRangeKind Kind);
- StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc,
- SourceLocation CoawaitLoc,
- Stmt *InitStmt,
- SourceLocation ColonLoc,
- Stmt *RangeDecl, Stmt *Begin, Stmt *End,
- Expr *Cond, Expr *Inc,
- Stmt *LoopVarDecl,
- SourceLocation RParenLoc,
- BuildForRangeKind Kind);
+ StmtResult ActOnCXXForRangeStmt(
+ Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc,
+ Stmt *InitStmt, Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection,
+ SourceLocation RParenLoc, BuildForRangeKind Kind,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
+ StmtResult BuildCXXForRangeStmt(
+ SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
+ SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End,
+ Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, SourceLocation RParenLoc,
+ BuildForRangeKind Kind,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
StmtResult FinishCXXForRangeStmt(Stmt *ForRange, Stmt *Body);
StmtResult ActOnGotoStmt(SourceLocation GotoLoc,
@@ -9985,6 +10019,18 @@ class Sema final {
return currentEvaluationContext().isImmediateFunctionContext();
}
+ bool isInLifetimeExtendingContext() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().InLifetimeExtendingContext;
+ }
+
+ bool isInMaterializeTemporaryObjectContext() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().InMaterializeTemporaryObjectContext;
+ }
+
bool isCheckingDefaultArgumentOrInitializer() const {
const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext();
return (Ctx.Context ==
@@ -10024,6 +10070,32 @@ class Sema final {
return Res;
}
+ /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext
+ /// flag from previous context.
+ void keepInLifetimeExtendingContext() {
+ if (ExprEvalContexts.size() > 2 &&
+ ExprEvalContexts[ExprEvalContexts.size() - 2]
+ .InLifetimeExtendingContext) {
+ auto &LastRecord = ExprEvalContexts.back();
+ auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+ LastRecord.InLifetimeExtendingContext =
+ PrevRecord.InLifetimeExtendingContext;
+ }
+ }
+
+ /// keepInMaterializeTemporaryObjectContext - Pull down
+ /// InMaterializeTemporaryObjectContext flag from previous context.
+ void keepInMaterializeTemporaryObjectContext() {
+ if (ExprEvalContexts.size() > 2 &&
+ ExprEvalContexts[ExprEvalContexts.size() - 2]
+ .InMaterializeTemporaryObjectContext) {
+ auto &LastRecord = ExprEvalContexts.back();
+ auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+ LastRecord.InMaterializeTemporaryObjectContext =
+ PrevRecord.InMaterializeTemporaryObjectContext;
+ }
+ }
+
/// RAII class used to determine whether SFINAE has
/// trapped any errors that occur during template argument
/// deduction.
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 1b91c86f91398cc..877e205e2e9bfab 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -643,7 +643,9 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
: "200704");
Builder.defineMacro("__cpp_constexpr_in_decltype", "201711L");
Builder.defineMacro("__cpp_range_based_for",
- LangOpts.CPlusPlus17 ? "201603L" : "200907");
+ LangOpts.CPlusPlus23 ? "202211L"
+ : LangOpts.CPlusPlus17 ? "201603L"
+ : "200907");
Builder.defineMacro("__cpp_static_assert", LangOpts.CPlusPlus26 ? "202306L"
: LangOpts.CPlusPlus17
? "201411L"
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 356e7851ec639c1..3470caef07d8728 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2312,12 +2312,38 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
bool IsForRangeLoop = false;
if (TryConsumeToken(tok::colon, FRI->ColonLoc)) {
IsForRangeLoop = true;
+ EnterExpressionEvaluationContext ForRangeInitContext(
+ Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+ /*LambdaContextDecl=*/nullptr,
+ Sema::ExpressionEvaluationContextRecord::EK_Other,
+ getLangOpts().CPlusPlus23);
+
+ // P2718R0 - Lifetime extension in range-based for loops.
+ if (getLangOpts().CPlusPlus23) {
+ auto &LastRecord = Actions.ExprEvalContexts.back();
+ LastRecord.InLifetimeExtendingContext = true;
+
+ // Materialize non-`cv void` prvalue temporaries in discarded
+ // expressions. These materialized temporaries may be lifetime-extented.
+ LastRecord.InMaterializeTemporaryObjectContext = true;
+ }
+
if (getLangOpts().OpenMP)
Actions.startOpenMPCXXRangeFor();
if (Tok.is(tok::l_brace))
FRI->RangeExpr = ParseBraceInitializer();
else
FRI->RangeExpr = ParseExpression();
+
+ // Before c++23, ForRangeLifetimeExtendTemps should be empty.
+ assert(
+ getLangOpts().CPlusPlus23 ||
+ Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
+
+ // Move the collected materialized temporaries into ForRangeInit before
+ // ForRangeInitContext exit.
+ FRI->LifetimeExtendTemps = std::move(
+ Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
}
Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index d0ff33bd1379ab7..76a3fa8f2627de2 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2288,11 +2288,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
ForRangeStmt = Actions.ActOnCXXForRangeStmt(
getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(),
ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc, CorrectedRange.get(),
- T.getCloseLocation(), Sema::BFRK_Build);
-
- // Similarly, we need to do the semantic analysis for a for-range
- // statement immediately in order to close over temporaries correctly.
+ T.getCloseLocation(), Sema::BFRK_Build,
+ ForRangeInfo.LifetimeExtendTemps);
} else if (ForEach) {
+ // Similarly, we need to do the semantic analysis for a for-range
+ // statement immediately in order to close over temporaries correctly.
ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc,
FirstPart.get(),
Collection.get(),
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 790ea217ef8190d..0a9e067c3fc1d81 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6256,7 +6256,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
assert(Param->hasDefaultArg() && "can't build nonexistent default arg");
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+ bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
std::optional<ExpressionEvaluationContextRecord::InitializationContext>
InitializationContext =
OutermostDeclarationWithDelayedImmediateInvocations();
@@ -6289,9 +6289,17 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
ImmediateCallVisitor V(getASTContext());
if (!NestedDefaultChecking)
V.TraverseDecl(Param);
- if (V.HasImmediateCalls) {
- ExprEvalContexts.back().DelayedDefaultInitializationContext = {
- CallLoc, Param, CurContext};
+
+ // Rewrite the call argument that was created from the corresponding
+ // parameter's default argument.
+ if (V.HasImmediateCalls || InLifetimeExtendingContext) {
+ 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();
+ keepInMaterializeTemporaryObjectContext();
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
runWithSufficientStackSpace(CallLoc, [&] {
@@ -6337,7 +6345,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
Expr *Init = nullptr;
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
-
+ bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
EnterExpressionEvaluationContext EvalContext(
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
@@ -6372,12 +6380,16 @@ 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();
+ keepInMaterializeTemporaryObjectContext();
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
runWithSufficientStackSpace(Loc, [&] {
@@ -18652,6 +18664,16 @@ void Sema::PopExpressionEvaluationContext() {
}
}
+ // Append the collected materialized temporaries into previous context before
+ // exit if the previous also is a lifetime extending context.
+ auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+ if (getLangOpts().CPlusPlus23 && isInLifetimeExtendingContext() &&
+ PrevRecord.InLifetimeExtendingContext && !ExprEvalContexts.empty()) {
+ auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+ PrevRecord.ForRangeLifetimeExtendTemps.append(
+ Rec.ForRangeLifetimeExtendTemps);
+ }
+
WarnOnPendingNoDerefs(Rec);
HandleImmediateInvocations(*this, Rec);
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 953bfe484a52802..011b2b6e8d4bacb 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8206,21 +8206,6 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
E = result.get();
}
- // C99 6.3.2.1:
- // [Except in specific positions,] an lvalue that does not have
- // array type is converted to the value stored in the
- // designated object (and is no longer an lvalue).
- if (E->isPRValue()) {
- // In C, function designators (i.e. expressions of function type)
- // are r-values, but we still want to do function-to-pointer decay
- // on them. This is both technically correct and convenient for
- // some clients.
- if (!getLangOpts().CPlusPlus && E->getType()->isFunctionType())
- return DefaultFunctionArrayConversion(E);
-
- return E;
- }
-
if (getLangOpts().CPlusPlus) {
// The C++11 standard defines the notion of a discarded-value expression;
// normally, we don't need to do anything to handle it, but if it is a
@@ -8241,11 +8226,32 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
// If the expression is a prvalue after this optional conversion, the
// temporary materialization conversion is applied.
//
- // We skip this step: IR generation is able to synthesize the storage for
- // itself in the aggregate case, and adding the extra node to the AST is
- // just clutter.
- // FIXME: We don't emit lifetime markers for the temporaries due to this.
- // FIXME: Do any other AST consumers care about this?
+ // We do not materialize temporaries by default in order to avoid creating
+ // unnecessary temporary objects. If we skip this step, IR generation is
+ // able to synthesize the storage for itself in the aggregate case, and
+ // adding the extra node to the AST is just clutter.
+ if (isInMaterializeTemporaryObjectContext() && getLangOpts().CPlusPlus17 &&
+ E->isPRValue() && !E->getType()->isVoidType()) {
+ ExprResult Res = TemporaryMaterializationConversion(E);
+ if (Res.isInvalid())
+ return E;
+ E = Res.get();
+ }
+ return E;
+ }
+
+ // C99 6.3.2.1:
+ // [Except in specific positions,] an lvalue that does not have
+ // array type is converted to the value stored in the
+ // designated object (and is no longer an lvalue).
+ if (E->isPRValue()) {
+ // In C, function designators (i.e. expressions of function type)
+ // are r-values, but we still want to do function-to-pointer decay
+ // on them. This is both technically correct and convenient for
+ // some clients.
+ if (!getLangOpts().CPlusPlus && E->getType()->isFunctionType())
+ return DefaultFunctionArrayConversion(E);
+
return E;
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 457fa377355a97c..b6de06464cd6f31 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8491,6 +8491,10 @@ 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);
+ }
return MTE;
}
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 9e7c8c7e4e8c12c..28f75196163cca4 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -27,6 +27,7 @@
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
@@ -2489,11 +2490,11 @@ static bool ObjCEnumerationCollection(Expr *Collection) {
///
/// The body of the loop is not available yet, since it cannot be analysed until
/// we have determined the type of the for-range-declaration.
-StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
- SourceLocation CoawaitLoc, Stmt *InitStmt,
- Stmt *First, SourceLocation ColonLoc,
- Expr *Range, SourceLocation RParenLoc,
- BuildForRangeKind Kind) {
+StmtResult Sema::ActOnCXXForRangeStmt(
+ Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
+ Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc,
+ BuildForRangeKind Kind,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
// FIXME: recover in order to allow the body to be parsed.
if (!First)
return StmtError();
@@ -2557,7 +2558,8 @@ StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
StmtResult R = BuildCXXForRangeStmt(
ForLoc, CoawaitLoc, InitStmt, ColonLoc, RangeDecl.get(),
/*BeginStmt=*/nullptr, /*EndStmt=*/nullptr,
- /*Cond=*/nullptr, /*Inc=*/nullptr, DS, RParenLoc, Kind);
+ /*Cond=*/nullptr, /*Inc=*/nullptr, DS, RParenLoc, Kind,
+ LifetimeExtendTemps);
if (R.isInvalid()) {
ActOnInitializerError(LoopVar);
return StmtError();
@@ -2747,13 +2749,12 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
}
/// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement.
-StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
- SourceLocation CoawaitLoc, Stmt *InitStmt,
- SourceLocation ColonLoc, Stmt *RangeDecl,
- Stmt *Begin, Stmt *End, Expr *Cond,
- Expr *Inc, Stmt *LoopVarDecl,
- SourceLocation RParenLoc,
- BuildForRangeKind Kind) {
+StmtResult Sema::BuildCXXForRangeStmt(
+ SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
+ SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End,
+ Expr *Cond, Expr *Inc, Stmt *LoopVarDecl, SourceLocation RParenLoc,
+ BuildForRangeKind Kind,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
// FIXME: This should not be used during template instantiation. We should
// pick up the set of unqualified lookup results for the != and + operators
// in the initial parse.
@@ -2813,6 +2814,14 @@ StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
diag::err_for_range_incomplete_type))
return StmtError();
+ // P2718R0 - Lifetime extension in range-based for loops.
+ if (getLangOpts().CPlusPlus23 && !LifetimeExtendTemps.empty()) {
+ InitializedEntity Entity =
+ InitializedEntity::InitializeVariable(RangeVar);
+ for (auto *MTE : LifetimeExtendTemps)
+ MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
+ }
+
// Build auto __begin = begin-expr, __end = end-expr.
// Divide by 2, since the variables are in the inner scope (loop body).
const auto DepthStr = std::to_string(S->getDepth() / 2);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index fbc8572ea0e0f0c..fcb27a880290b85 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5440,6 +5440,8 @@ void Sema::InstantiateVariableInitializer(
EnterExpressionEvaluationContext Evaluated(
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var);
+ keepInLifetimeExtendingContext();
+ keepInMaterializeTemporaryObjectContext();
// Instantiate the initializer.
ExprResult Init;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 40028544e23c8e1..c412a3a75ee88c4 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -2544,12 +2544,11 @@ class TreeTransform {
///
/// By default, performs semantic analysis to build the new statement.
/// Subclasses may override this routine to provide different behavior.
- StmtResult RebuildCXXForRangeStmt(SourceLocation ForLoc,
- SourceLocation CoawaitLoc, Stmt *Init,
- SourceLocation ColonLoc, Stmt *Range,
- Stmt *Begin, Stmt *End, Expr *Cond,
- Expr *Inc, Stmt *LoopVar,
- SourceLocation RParenLoc) {
+ StmtResult RebuildCXXForRangeStmt(
+ SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *Init,
+ SourceLocation ColonLoc, Stmt *Range, Stmt *Begin, Stmt *End, Expr *Cond,
+ Expr *Inc, Stmt *LoopVar, SourceLocation RParenLoc,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
// If we've just learned that the range is actually an Objective-C
// collection, treat this as an Objective-C fast enumeration loop.
if (DeclStmt *RangeStmt = dyn_cast<DeclStmt>(Range)) {
@@ -2575,9 +2574,9 @@ class TreeTransform {
}
}
- return getSema().BuildCXXForRangeStmt(ForLoc, CoawaitLoc, Init, ColonLoc,
- Range, Begin, End, Cond, Inc, LoopVar,
- RParenLoc, Sema::BFRK_Rebuild);
+ return getSema().BuildCXXForRangeStmt(
+ ForLoc, CoawaitLoc, Init, ColonLoc, Range, Begin, End, Cond, Inc,
+ LoopVar, RParenLoc, Sema::BFRK_Rebuild, LifetimeExtendTemps);
}
/// Build a new C++0x range-based for statement.
@@ -4135,6 +4134,8 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init,
getSema(), EnterExpressionEvaluationContext::InitList,
Construct->isListInitialization());
+ getSema().keepInLifetimeExtendingContext();
+ getSema().keepInLifetimeExtendingContext();
SmallVector<Expr*, 8> NewArgs;
bool ArgChanged = false;
if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(),
@@ -8546,6 +8547,21 @@ StmtResult TreeTransform<Derived>::TransformCXXTryStmt(CXXTryStmt *S) {
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
+ EnterExpressionEvaluationContext ForRangeInitContext(
+ getSema(), Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+ /*LambdaContextDecl=*/nullptr,
+ Sema::ExpressionEvaluationContextRecord::EK_Other,
+ getSema().getLangOpts().CPlusPlus23);
+
+ // P2718R0 - Lifetime extension in range-based for loops.
+ if (getSema().getLangOpts().CPlusPlus23) {
+ auto &LastRecord = getSema().ExprEvalContexts.back();
+ LastRecord.InLifetimeExtendingContext = true;
+
+ // Materialize non-`cv void` prvalue temporaries in discarded
+ // expressions. These materialized temporaries may be lifetime-extented.
+ LastRecord.InMaterializeTemporaryObjectContext = true;
+ }
StmtResult Init =
S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult();
if (Init.isInvalid())
@@ -8555,6 +8571,12 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
if (Range.isInvalid())
return StmtError();
+ // Before c++23, ForRangeLifetimeExtendTemps should be empty.
+ assert(getSema().getLangOpts().CPlusPlus23 ||
+ getSema().ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
+ auto ForRangeLifetimeExtendTemps =
+ getSema().ExprEvalContexts.back().ForRangeLifetimeExtendTemps;
+
StmtResult Begin = getDerived().TransformStmt(S->getBeginStmt());
if (Begin.isInvalid())
return StmtError();
@@ -8591,13 +8613,10 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
Cond.get() != S->getCond() ||
Inc.get() != S->getInc() ||
LoopVar.get() != S->getLoopVarStmt()) {
- NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(),
- S->getCoawaitLoc(), Init.get(),
- S->getColonLoc(), Range.get(),
- Begin.get(), End.get(),
- Cond.get(),
- Inc.get(), LoopVar.get(),
- S->getRParenLoc());
+ NewStmt = getDerived().RebuildCXXForRangeStmt(
+ S->getForLoc(), S->getCoawaitLoc(), Init.get(), S->getColonLoc(),
+ Range.get(), Begin.get(), End.get(), Cond.get(), Inc.get(),
+ LoopVar.get(), S->getRParenLoc(), ForRangeLifetimeExtendTemps);
if (NewStmt.isInvalid() && LoopVar.get() != S->getLoopVarStmt()) {
// Might not have attached any initializer to the loop variable.
getSema().ActOnInitializerError(
@@ -8613,13 +8632,10 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
// Body has changed but we didn't rebuild the for-range statement. Rebuild
// it now so we have a new statement to attach the body to.
if (Body.get() != S->getBody() && NewStmt.get() == S) {
- NewStmt = getDerived().RebuildCXXForRangeStmt(S->getForLoc(),
- S->getCoawaitLoc(), Init.get(),
- S->getColonLoc(), Range.get(),
- Begin.get(), End.get(),
- Cond.get(),
- Inc.get(), LoopVar.get(),
- S->getRParenLoc());
+ NewStmt = getDerived().RebuildCXXForRangeStmt(
+ S->getForLoc(), S->getCoawaitLoc(), Init.get(), S->getColonLoc(),
+ Range.get(), Begin.get(), End.get(), Cond.get(), Inc.get(),
+ LoopVar.get(), S->getRParenLoc(), ForRangeLifetimeExtendTemps);
if (NewStmt.isInvalid())
return StmtError();
}
diff --git a/clang/test/AST/ast-dump-for-range-lifetime.cpp b/clang/test/AST/ast-dump-for-range-lifetime.cpp
new file mode 100644
index 000000000000000..dec2e29184526ee
--- /dev/null
+++ b/clang/test/AST/ast-dump-for-range-lifetime.cpp
@@ -0,0 +1,355 @@
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-linux-gnu -fcxx-exceptions -ast-dump %s \
+// RUN: | FileCheck -strict-whitespace %s
+
+namespace P2718R0 {
+
+// Test basic
+struct A {
+ int a[3] = {1, 2, 3};
+ A() {}
+ ~A() {}
+ const int *begin() const { return a; }
+ const int *end() const { return a + 3; }
+ A& r() { return *this; }
+ A g() { return A(); }
+};
+
+A g() { return A(); }
+const A &f1(const A &t) { return t; }
+
+void test1() {
+ [[maybe_unused]] int sum = 0;
+ // CHECK: FunctionDecl {{.*}} test1 'void ()'
+ // CHECK: | `-CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: | |-<<<NULL>>>
+ // CHECK-NEXT: | |-DeclStmt {{.*}}
+ // CHECK-NEXT: | | `-VarDecl {{.*}} implicit used __range1 'const A &' cinit
+ // CHECK-NEXT: | | `-ExprWithCleanups {{.*}} 'const A':'const P2718R0::A' lvalue
+ // CHECK-NEXT: | | `-CallExpr {{.*}} 'const A':'const P2718R0::A' lvalue
+ // CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const A &(*)(const A &)' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'const A &(const A &)' lvalue Function {{.*}} 'f1' 'const A &(const A &)'
+ // CHECK-NEXT: | | `-MaterializeTemporaryExpr {{.*}} 'const A':'const P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'const A &'
+ // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const A':'const P2718R0::A' <NoOp>
+ // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | `-CallExpr {{.*}} 'A':'P2718R0::A'
+ // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'A (*)()' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'A ()' lvalue Function {{.*}} 'g' 'A ()'
+ for (auto e : f1(g()))
+ sum += e;
+}
+
+struct B : A {};
+int (&f(const A *))[3];
+const A *g(const A &);
+void bar(int) {}
+
+void test2() {
+ // CHECK: FunctionDecl {{.*}} test2 'void ()'
+ // CHECK: | `-CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: | |-<<<NULL>>>
+ // CHECK-NEXT: | |-DeclStmt {{.*}}
+ // CHECK-NEXT: | | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+ // CHECK-NEXT: | | `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | | `-CallExpr {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'int (&(*)(const A *))[3]' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'int (&(const A *))[3]' lvalue Function {{.*}} 'f' 'int (&(const A *))[3]'
+ // CHECK-NEXT: | | `-CallExpr {{.*}} 'const A *'
+ // CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const A *(*)(const A &)' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'const A *(const A &)' lvalue Function {{.*}} 'g' 'const A *(const A &)'
+ // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const A':'const P2718R0::A' lvalue <DerivedToBase (A)>
+ // CHECK-NEXT: | | `-MaterializeTemporaryExpr {{.*}} 'const B':'const P2718R0::B' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const B':'const P2718R0::B' <NoOp>
+ // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'B':'P2718R0::B' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | `-CXXTemporaryObjectExpr {{.*}} 'B':'P2718R0::B' 'void () noexcept(false)' zeroing
+ for (auto e : f(g(B())))
+ bar(e);
+}
+
+// Test discard statement.
+struct LockGuard {
+ LockGuard() {}
+ ~LockGuard() {}
+};
+
+void test3() {
+ int v[] = {42, 17, 13};
+
+ // CHECK: FunctionDecl {{.*}} test3 'void ()'
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+ // CHECK-NEXT: | |-CXXStaticCastExpr {{.*}} 'void' static_cast<void> <ToVoid>
+ // CHECK-NEXT: | | `-MaterializeTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' xvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+ for ([[maybe_unused]] int x : static_cast<void>(LockGuard()), v)
+ LockGuard guard;
+
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+ // CHECK-NEXT: | |-CStyleCastExpr {{.*}} 'void' <ToVoid>
+ // CHECK-NEXT: | | `-MaterializeTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' xvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+ for ([[maybe_unused]] int x : (void)LockGuard(), v)
+ LockGuard guard;
+
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+ // CHECK-NEXT: | |-MaterializeTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' xvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+ for ([[maybe_unused]] int x : LockGuard(), v)
+ LockGuard guard;
+}
+
+// Test default arg
+int (&default_arg_fn(const A & = A()))[3];
+void test4() {
+
+ // CHECK: FunctionDecl {{.*}} test4 'void ()'
+ // FIXME: Should dump CXXDefaultArgExpr->getExpr() if CXXDefaultArgExpr has been rewrited?
+ for (auto e : default_arg_fn())
+ bar(e);
+}
+
+struct DefaultA {
+ DefaultA() {}
+ ~DefaultA() {}
+};
+
+A foo(const A&, const DefaultA &Default = DefaultA()) {
+ return A();
+}
+
+void test5() {
+ for (auto e : default_arg_fn(foo(foo(foo(A())))))
+ bar(e);
+}
+
+struct C : public A {
+ C() {}
+ C(int, const C &, const DefaultA & = DefaultA()) {}
+};
+
+void test6() {
+ for (auto e : C(0, C(0, C(0, C()))))
+ bar(e);
+}
+
+// Test member call
+void test7() {
+ // CHECK: FunctionDecl {{.*}} test7 'void ()'
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'A &&' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'A':'P2718R0::A' xvalue
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CallExpr {{.*}} 'A':'P2718R0::A'
+ // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'A (*)()' <FunctionToPointerDecay>
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'A ()' lvalue Function {{.*}} 'g' 'A ()'
+ for (auto e : g().r().g().r().g().r().g())
+ bar(e);
+}
+
+// Test basic && dependent context
+template <typename T> T dg() { return T(); }
+template <typename T> const T &df1(const T &t) { return t; }
+
+void test8() {
+ [[maybe_unused]] int sum = 0;
+ // CHECK: FunctionDecl {{.*}} test8 'void ()'
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'const P2718R0::A &' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'const P2718R0::A' lvalue
+ // CHECK-NEXT: | `-CallExpr {{.*}} 'const P2718R0::A' lvalue
+ // CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'const P2718R0::A &(*)(const P2718R0::A &)' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'const P2718R0::A &(const P2718R0::A &)' lvalue Function {{.*}} 'df1' 'const P2718R0::A &(const P2718R0::A &)' (FunctionTemplate {{.*}} 'df1')
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'const P2718R0::A &'
+ // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const P2718R0::A' <NoOp>
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CallExpr {{.*}} 'P2718R0::A'
+ // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'P2718R0::A (*)()' <FunctionToPointerDecay>
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'P2718R0::A ()' lvalue Function {{.*}} 'dg' 'P2718R0::A ()' (FunctionTemplate {{.*}} 'dg')
+ for (auto e : df1(dg<A>()))
+ sum += e;
+}
+
+template <typename T> int (&df2(const T *))[3];
+const A *dg2(const A &);
+
+void test9() {
+ // CHECK: FunctionDecl {{.*}} test9 'void ()'
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | `-CallExpr {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'int (&(*)(const P2718R0::A *))[3]' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'int (&(const P2718R0::A *))[3]' lvalue Function {{.*}} 'df2' 'int (&(const P2718R0::A *))[3]' (FunctionTemplate {{.*}} 'df2')
+ // CHECK-NEXT: | `-CallExpr {{.*}} 'const A *'
+ // CHECK-NEXT: | |-ImplicitCastExpr {{.*}} 'const A *(*)(const A &)' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'const A *(const A &)' lvalue Function {{.*}} 'dg2' 'const A *(const A &)'
+ // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const A':'const P2718R0::A' lvalue <DerivedToBase (A)>
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const B':'const P2718R0::B' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const B':'const P2718R0::B' <NoOp>
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'B':'P2718R0::B' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CXXTemporaryObjectExpr {{.*}} 'B':'P2718R0::B' 'void () noexcept(false)' zeroing
+ for (auto e : df2(dg2(B())))
+ bar(e);
+}
+
+// Test discard statement && dependent context
+void test10() {
+ int v[] = {42, 17, 13};
+
+ // CHECK: FunctionDecl {{.*}} test10 'void ()'
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+ // CHECK-NEXT: | |-CXXStaticCastExpr {{.*}} 'void' static_cast<void> <ToVoid>
+ // CHECK-NEXT: | | `-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue
+ // CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' lvalue Function {{.*}} 'df1' 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' (FunctionTemplate {{.*}} 'df1')
+ // CHECK-NEXT: | | `-MaterializeTemporaryExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' <NoOp>
+ // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+ for ([[maybe_unused]] int x : static_cast<void>(df1(LockGuard())), v)
+ LockGuard guard;
+
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+ // CHECK-NEXT: | |-CStyleCastExpr {{.*}} 'void' <ToVoid>
+ // CHECK-NEXT: | | `-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue
+ // CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' lvalue Function {{.*}} 'df1' 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' (FunctionTemplate {{.*}} 'df1')
+ // CHECK-NEXT: | | `-MaterializeTemporaryExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' <NoOp>
+ // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+ for ([[maybe_unused]] int x : (void)df1(LockGuard()), v)
+ LockGuard guard;
+
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'int (&)[3]' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'int[3]' lvalue
+ // CHECK-NEXT: | `-BinaryOperator {{.*}} 'int[3]' lvalue ','
+ // CHECK-NEXT: | |-BinaryOperator {{.*}} 'const P2718R0::LockGuard' lvalue ','
+ // CHECK-NEXT: | | |-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue
+ // CHECK-NEXT: | | | |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | | | `-DeclRefExpr {{.*}} 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' lvalue Function {{.*}} 'df1' 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' (FunctionTemplate {{.*}} 'df1')
+ // CHECK-NEXT: | | | `-MaterializeTemporaryExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | | | `-ImplicitCastExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' <NoOp>
+ // CHECK-NEXT: | | | `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | | `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+ // CHECK-NEXT: | | `-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue
+ // CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' <FunctionToPointerDecay>
+ // CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' lvalue Function {{.*}} 'df1' 'const P2718R0::LockGuard &(const P2718R0::LockGuard &)' (FunctionTemplate {{.*}} 'df1')
+ // CHECK-NEXT: | | `-MaterializeTemporaryExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]'
+ // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const LockGuard':'const P2718R0::LockGuard' <NoOp>
+ // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | | `-CXXTemporaryObjectExpr {{.*}} 'LockGuard':'P2718R0::LockGuard' 'void ()'
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int[3]' lvalue Var {{.*}} 'v' 'int[3]'
+ for ([[maybe_unused]] int x : df1(LockGuard()), df1(LockGuard()), v)
+ LockGuard guard;
+}
+
+// Test default argument && dependent context
+template <typename T> int (&default_arg_fn2(const T & = T()))[3];
+void test11() {
+ for (auto e : default_arg_fn2<A>())
+ bar(e);
+}
+
+template <typename T> A foo2(const T&, const DefaultA &Default = DefaultA());
+
+void test12() {
+ for (auto e : default_arg_fn2(foo2(foo2(foo2(A())))))
+ bar(e);
+}
+
+// Test member call && dependent context
+void test13() {
+
+ // CHECK: FunctionDecl {{.*}} test13 'void ()'
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'A &&' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'A':'P2718R0::A' xvalue
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
+ // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
+ // CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'P2718R0::A' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-CallExpr {{.*}} 'P2718R0::A'
+ // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'P2718R0::A (*)()' <FunctionToPointerDecay>
+ // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'P2718R0::A ()' lvalue Function {{.*}} 'dg' 'P2718R0::A ()' (FunctionTemplate {{.*}} 'dg')
+ for (auto e : dg<A>().r().g().r().g().r().g())
+ bar(e);
+}
+} // namespace P2718R0
diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp
index 077385fb7aaaf76..5554363cc69abb5 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++17 %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s --implicit-check-not='call{{.*}}dtor'
+// RUN: %clang_cc1 -std=c++23 %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s --check-prefixes=CHECK-CXX23,CHECK-CXX23-NEXT,CHECK-CXX23-LABEL
namespace std {
typedef decltype(sizeof(int)) size_t;
@@ -9,7 +10,36 @@ namespace std {
size_t size;
initializer_list() : begin(nullptr), size(0) {}
};
-}
+
+ template <typename E>
+ struct list {
+ list() {}
+ ~list() {}
+ E *begin();
+ E *end();
+ const E *begin() const;
+ const E *end() const;
+ };
+
+ template <typename E>
+ struct vector {
+ vector() {}
+ vector(std::initializer_list<E>) {}
+ ~vector() {}
+ E *begin();
+ E *end();
+ const E *begin() const;
+ const E *end() const;
+ };
+
+ template <typename T>
+ struct lock_guard {
+ lock_guard(T) {}
+ ~lock_guard() {}
+ };
+
+ struct mutex {};
+} // namespace std
void then();
@@ -238,3 +268,282 @@ void init_capture_init_list() {
// CHECK: call {{.*}}dtor
// CHECK: }
}
+
+namespace P2718R0 {
+namespace basic {
+template <typename E> using T2 = std::list<E>;
+template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; }
+template <typename E> const T2<E> &f2_temp(T2<E> t) { return t; }
+template <typename E> T2<E> g_temp() { return T2<E>{}; }
+
+template <typename E>
+void foo_dependent_context1() {
+ // CHECK-CXX23: void @_ZN7P2718R05basic22foo_dependent_context1IiEEvv()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ for (auto e : f1_temp(g_temp<E>())) {} // OK, lifetime of return value of g() extended
+}
+
+template <typename E>
+void foo_dependent_context2() {
+ // CHECK-CXX23: void @_ZN7P2718R05basic22foo_dependent_context2IiEEvv()
+ // CHECK-CXX23-NEXT: entry:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R05basic6g_tempIiEESt4listIT_Ev(
+ // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R05basic7f2_tempIiEERKSt4listIT_ES4_(
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ // CHECK-CXX23: call {{.*}} @_ZNKSt4listIiE5beginEv(
+ // CHECK-CXX23: call {{.*}} @_ZNKSt4listIiE3endEv(
+ for (auto e : f2_temp(g_temp<E>())) {} // undefined behavior
+}
+
+template void foo_dependent_context1<int>();
+template void foo_dependent_context2<int>();
+} // namespace basic
+
+namespace discard_value_expression {
+template <typename T>
+void f_dependent_context1() {
+ std::vector<T> v = { 42, 17, 13 };
+ std::mutex m;
+ // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context1IiEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+ for (T x : std::lock_guard<std::mutex>(m), v) // lock released in C++ 2023
+ std::lock_guard<std::mutex> guard(m); // OK in C++ 2023, now deadlocks
+}
+
+template <typename T>
+void f_dependent_context2() {
+ std::vector<T> v = { 42, 17, 13 };
+ std::mutex m;
+ // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context2IiEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+ for (T x : (void)std::lock_guard<std::mutex>(m), v) // lock released in C++ 2023
+ std::lock_guard<std::mutex> guard(m); // OK in C++ 2023, now deadlocks
+}
+
+template <typename T>
+void f_dependent_context3() {
+ std::vector<T> v = { 42, 17, 13 };
+ std::mutex m;
+ // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression20f_dependent_context3IiEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+ for (T x : static_cast<void>(std::lock_guard<std::mutex>(m)), v) // lock released in C++ 2023
+ std::lock_guard<std::mutex> guard(m); // OK in C++ 2023, now deadlocks
+}
+
+template void f_dependent_context1<int>();
+template void f_dependent_context2<int>();
+template void f_dependent_context3<int>();
+} // namespace discard_value_expression
+
+namespace member_call {
+template <typename T>
+struct ListWrapper {
+ std::list<T> list;
+ ListWrapper() {}
+ ~ListWrapper() {}
+ const T *begin() const { return list.begin(); }
+ const T *end() const { return list.end(); }
+ ListWrapper& r() { return *this; }
+ ListWrapper g() { return ListWrapper(); }
+};
+
+template <typename E>
+ListWrapper<E> g_temp() { return ListWrapper<E>{}; }
+
+template <typename T>
+void member_call_dependent_context() {
+ // CHECK-CXX23: void @_ZN7P2718R011member_call29member_call_dependent_contextIiEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+ for (auto e : g_temp<T>().r().g().r().g().r().g()) {}
+}
+
+template void member_call_dependent_context<int>();
+} // namespace member_call
+
+namespace default_arg {
+template <typename T>
+struct DefaultArg {
+ DefaultArg() {}
+ DefaultArg(int) {}
+ ~DefaultArg() {}
+};
+
+template <typename T>
+struct C2 : public std::list<T> {
+ C2() {}
+ C2(int, const C2 &, const DefaultArg<T> &Default = DefaultArg<T>{}) {}
+};
+
+template <typename T>
+std::list<T> temp_foo(const std::list<T>&, const DefaultArg<T> &Default = DefaultArg<T>{}) {
+ return std::list<T>{};
+}
+
+template <typename T>
+void default_arg_dependent_context1() {
+ // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context1IiEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ for (auto e : temp_foo(std::list<T>{})) {}
+}
+
+template <typename T>
+void default_arg_dependent_context2() {
+ // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context2IiEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ for (auto e : temp_foo(temp_foo(std::list<T>{}))) {}
+}
+
+template <typename T>
+void default_arg_dependent_context3() {
+ // CHECK-CXX23: void @_ZN7P2718R011default_arg30default_arg_dependent_context3IiEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg2C2IiED1Ev(
+
+ for (auto e : C2<T>(0, C2<T>(0, C2<T>(0, C2<T>())))) {}
+}
+
+template void default_arg_dependent_context1<int>();
+template void default_arg_dependent_context2<int>();
+template void default_arg_dependent_context3<int>();
+} // namespace default_arg
+
+namespace basic {
+using T = std::list<int>;
+const T& f1(const T& t) { return t; }
+const T& f2(T t) { return t; }
+T g() { return T{}; }
+
+void foo1() {
+ // CHECK-CXX23: void @_ZN7P2718R05basic4foo1Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended
+}
+
+void foo2() {
+ // CHECK-CXX23: void @_ZN7P2718R05basic4foo2Ev()
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R05basic1gEv(
+ // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R05basic2f2ESt4listIiE(
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ for (auto e : f2(g())) {} // undefined behavior
+}
+} // namespace basic
+
+namespace discard_value_expression {
+void f1() {
+ std::vector<int> v = { 42, 17, 13 };
+ std::mutex m;
+ // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f1Ev()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+ for (int x : std::lock_guard<std::mutex>(m), v) // lock released in C++ 2023
+ std::lock_guard<std::mutex> guard(m); // OK in C++ 2023, now deadlocks
+}
+
+void f2() {
+ std::vector<int> v = { 42, 17, 13 };
+ std::mutex m;
+ // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f2Ev()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+ for (int x : (void)std::lock_guard<std::mutex>(m), v) // lock released in C++ 2023
+ std::lock_guard<std::mutex> guard(m); // OK in C++ 2023, now deadlocks
+}
+
+void f3() {
+ std::vector<int> v = { 42, 17, 13 };
+ std::mutex m;
+ // CHECK-CXX23: void @_ZN7P2718R024discard_value_expression2f3Ev()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt10lock_guardISt5mutexED1Ev(
+ for (int x : static_cast<void>(std::lock_guard<std::mutex>(m)), v) // lock released in C++ 2023
+ std::lock_guard<std::mutex> guard(m); // OK in C++ 2023, now deadlocks
+}
+} // namespace discard_value_expression
+
+namespace member_call {
+using A = ListWrapper<int>;
+
+A g() { return A(); }
+const A &f1(const A &t) { return t; }
+
+void member_call() {
+ // CHECK-CXX23: void @_ZN7P2718R011member_call11member_callEv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011member_call11ListWrapperIiED1Ev(
+ for (auto e : g().r().g().r().g().r().g()) {}
+}
+} // namespace member_call
+
+namespace default_arg {
+using A = std::list<int>;
+using DefaultA = DefaultArg<int>;
+struct C : public A {
+ C() {}
+ C(int, const C &, const DefaultA & = DefaultA()) {}
+};
+
+A foo(const A&, const DefaultA &Default = DefaultA()) {
+ return A();
+}
+
+int (&some_func(const A & = A{}))[3];
+
+void default_arg1() {
+ // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg1Ev()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ for (auto e : some_func()) {}
+}
+
+void default_arg2() {
+ // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg2Ev()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev(
+ for (auto e : some_func(foo(foo(A())))) {}
+}
+
+void default_arg3() {
+ // CHECK-CXX23: void @_ZN7P2718R011default_arg12default_arg3Ev()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg10DefaultArgIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R011default_arg1CD1Ev(
+ for (auto e : C(0, C(0, C(0, C())))) {}
+}
+} // namespace default_arg
+} // namespace P2718R0
+
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index eb0e615f9470b2d..2650a3a82252ba2 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -309,7 +309,7 @@
#error "wrong value for __cpp_constexpr"
#endif
-#if check(range_based_for, 0, 200907, 200907, 201603, 201603, 201603, 201603)
+#if check(range_based_for, 0, 200907, 200907, 201603, 201603, 202211, 202211)
#error "wrong value for __cpp_range_based_for"
#endif
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index a48f35eb5048378..a667a3cff744b74 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -429,7 +429,7 @@ <h2 id="cxx23">C++23 implementation status</h2>
<tr>
<td>Lifetime extension in range-based for loops</td>
<td><a href="https://wg21.link/P2718R0">P2718R0</a></td>
- <td class="none" align="center">No</td>
+ <td class="none" align="center">Clang 18</td>
</tr>
<!--Issaquah 2023 papers-->
<tr>
More information about the cfe-commits
mailing list