[clang] [Clang] Implement P2718R0 "Lifetime extension in range-based for loops" (PR #76361)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 25 08:15:38 PST 2023
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/76361
>From af44c06aabab3f6ae15d60b2473ff03934a6f39a Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Tue, 26 Dec 2023 00:15:25 +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/include/clang/Parse/Parser.h | 2 +-
clang/include/clang/Sema/Sema.h | 43 ++-
clang/lib/Parse/ParseDecl.cpp | 19 +
clang/lib/Parse/ParseStmt.cpp | 7 +-
clang/lib/Sema/SemaExpr.cpp | 22 +-
clang/lib/Sema/SemaExprCXX.cpp | 47 ++-
clang/lib/Sema/SemaInit.cpp | 4 +
clang/lib/Sema/SemaStmt.cpp | 17 +-
.../test/AST/ast-dump-for-range-lifetime.cpp | 355 ++++++++++++++++++
clang/test/CXX/special/class.temporary/p6.cpp | 234 ++++++++++++
10 files changed, 710 insertions(+), 40 deletions(-)
create mode 100644 clang/test/AST/ast-dump-for-range-lifetime.cpp
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 2dbe090bd0932f..a467ff6157962c 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2377,7 +2377,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 5e3b57ea33220b..fc2b3881d5c062 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1338,6 +1338,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 expression which need to
+ /// extend lifetime. Add MaterializeTemporaryExpr* if the value of
+ /// IsInLifetimeExtendingContext is true.
+ SmallVector<MaterializeTemporaryExpr *, 8> ForRangeLifetimeExtendTemps;
+
/// \brief Describes whether we are in an expression constext which we have
/// to handle differently.
enum ExpressionKind {
@@ -1357,6 +1363,19 @@ class Sema final {
// VLAs).
bool InConditionallyConstantEvaluateContext = false;
+ /// Whether we are currently in a context in which temporaries must be
+ /// lifetime-extended (Eg. in a for-range initializer).
+ bool IsInLifetimeExtendingContext = false;
+
+ /// Whether we should materialize temporaries in discarded expressions.
+ ///
+ /// [C++23][class.temporary]/p2.6 when a prvalue that has type other than cv
+ /// void appears as a discarded-value expression ([expr.context]).
+ ///
+ /// We do not materialize temporaries by default in order to avoid creating
+ /// unnecessary temporary objects.
+ bool MaterializePRValueInDiscardedExpression = 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
@@ -5209,13 +5228,11 @@ 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 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,
@@ -9956,6 +9973,18 @@ class Sema final {
return currentEvaluationContext().isImmediateFunctionContext();
}
+ bool isInLifetimeExtendingContext() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().IsInLifetimeExtendingContext;
+ }
+
+ bool ShouldMaterializePRValueInDiscardedExpression() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().MaterializePRValueInDiscardedExpression;
+ }
+
bool isCheckingDefaultArgumentOrInitializer() const {
const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext();
return (Ctx.Context ==
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index ed006f9d67de45..8b809aa9c3df26 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2312,12 +2312,31 @@ 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.IsInLifetimeExtendingContext = true;
+
+ // Materialize non-`cv void` prvalue temporaries in discarded
+ // expressions. These materialized temporaries may be lifetime-extented.
+ LastRecord.MaterializePRValueInDiscardedExpression = true;
+ }
+
if (getLangOpts().OpenMP)
Actions.startOpenMPCXXRangeFor();
if (Tok.is(tok::l_brace))
FRI->RangeExpr = ParseBraceInitializer();
else
FRI->RangeExpr = ParseExpression();
+ if (getLangOpts().CPlusPlus23)
+ 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 d0ff33bd1379ab..858b4f9fa9cc4f 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -2288,10 +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);
+ T.getCloseLocation(), Sema::BFRK_Build,
+ ForRangeInfo.LifetimeExtendTemps);
- // Similarly, we need to do the semantic analysis for a for-range
- // statement immediately in order to close over temporaries correctly.
+ // Similarly, we need to do the semantic analysis for a for-range
+ // statement immediately in order to close over temporaries correctly.
} else if (ForEach) {
ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc,
FirstPart.get(),
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 960f513d1111b2..45f6f50e6f7385 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 IsInLifetimeExtendingContext = isInLifetimeExtendingContext();
std::optional<ExpressionEvaluationContextRecord::InitializationContext>
InitializationContext =
OutermostDeclarationWithDelayedImmediateInvocations();
@@ -6289,9 +6289,19 @@ 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 || IsInLifetimeExtendingContext) {
+ if (V.HasImmediateCalls)
+ ExprEvalContexts.back().DelayedDefaultInitializationContext = {
+ CallLoc, Param, CurContext};
+ // Pass down lifetime extendning flag, and collect temporaries in
+ // CreateMaterializeTemporaryExpr when we rewrite the call argument.
+ auto &LastRecord = ExprEvalContexts.back();
+ auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2];
+ LastRecord.IsInLifetimeExtendingContext = IsInLifetimeExtendingContext;
+
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
runWithSufficientStackSpace(CallLoc, [&] {
@@ -6305,6 +6315,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
if (Res.isInvalid())
return ExprError();
Init = Res.get();
+
+ // Collect MaterializeTemporaryExprs in the rewrited CXXDefaultArgExpr.
+ PrevRecord.ForRangeLifetimeExtendTemps.append(
+ LastRecord.ForRangeLifetimeExtendTemps);
}
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 4ae04358d5df7c..8a44b2a704117f 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8209,21 +8209,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
@@ -8244,11 +8229,33 @@ 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 (ShouldMaterializePRValueInDiscardedExpression() &&
+ 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 61d244f3bb9798..42e4d1eacce78d 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8469,6 +8469,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 f0b03db690843a..f69a8a168fd20d 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2483,11 +2483,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();
@@ -2539,6 +2539,12 @@ StmtResult Sema::ActOnCXXForRangeStmt(Scope *S, SourceLocation ForLoc,
return StmtError();
}
+ if (getLangOpts().CPlusPlus23) {
+ auto Entity = InitializedEntity::InitializeVariable(RangeVar);
+ for (auto *MTE : LifetimeExtendTemps)
+ MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
+ }
+
// Claim the type doesn't contain auto: we've already done the checking.
DeclGroupPtrTy RangeGroup =
BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
@@ -2783,6 +2789,7 @@ StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc,
LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
}
} else if (!BeginDeclStmt.get()) {
+
SourceLocation RangeLoc = RangeVar->getLocation();
const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType();
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 00000000000000..dec2e29184526e
--- /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 077385fb7aaaf7..ff5ba7e563b064 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
namespace std {
typedef decltype(sizeof(int)) size_t;
@@ -238,3 +239,236 @@ void init_capture_init_list() {
// CHECK: call {{.*}}dtor
// CHECK: }
}
+
+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-CXX23: void @_ZN7P2718R05test1Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ 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-CXX23: void @_ZN7P2718R05test2Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01BD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for (auto e : f(g(B())))
+ bar(e);
+}
+
+// Test discard statement.
+struct LockGuard {
+ LockGuard() {}
+ ~LockGuard() {}
+};
+
+void test3() {
+ int v[] = {42, 17, 13};
+
+ // CHECK-CXX23: void @_ZN7P2718R05test3Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for ([[maybe_unused]] int x : static_cast<void>(LockGuard()), v)
+ LockGuard guard;
+
+ // CHECK-CXX23: for.cond.cleanup11:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end17
+ for ([[maybe_unused]] int x : (void)LockGuard(), v)
+ LockGuard guard;
+
+ // CHECK-CXX23: for.cond.cleanup27:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end33
+ for ([[maybe_unused]] int x : LockGuard(), v)
+ LockGuard guard;
+}
+
+// Test default arg
+int (&default_arg_fn(const A & = A()))[3];
+void test4() {
+
+ // CHECK-CXX23: void @_ZN7P2718R05test4Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for (auto e : default_arg_fn())
+ bar(e);
+}
+
+struct DefaultA {
+ DefaultA() {}
+ ~DefaultA() {}
+};
+
+A foo(const A&, const DefaultA &Default = DefaultA()) {
+ return A();
+}
+
+void test5() {
+ // CHECK-CXX23: void @_ZN7P2718R05test5Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ 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() {
+ // CHECK-CXX23: void @_ZN7P2718R05test6Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01CD1Ev(
+ // CHECK-CXX23: br label %for.end
+ for (auto e : C(0, C(0, C(0, C()))))
+ bar(e);
+}
+
+// Test member call
+void test7() {
+ // CHECK-CXX23: void @_ZN7P2718R05test7Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ 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-CXX23: void @_ZN7P2718R05test8Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for (auto e : df1(dg<A>()))
+ sum += e;
+}
+
+template <typename T> int (&df2(const T *))[3];
+const A *dg2(const A &);
+
+void test9() {
+ // CHECK-CXX23: void @_ZN7P2718R05test9Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01BD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for (auto e : df2(dg2(B())))
+ bar(e);
+}
+
+// Test discard statement && dependent context
+void test10() {
+ int v[] = {42, 17, 13};
+
+ // CHECK-CXX23: void @_ZN7P2718R06test10Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for ([[maybe_unused]] int x : static_cast<void>(df1(LockGuard())), v)
+ LockGuard guard;
+
+ // CHECK-CXX23: for.cond.cleanup12:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.inc16
+ for ([[maybe_unused]] int x : (void)df1(LockGuard()), v)
+ LockGuard guard;
+
+ // CHECK-CXX23: for.cond.cleanup31:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R09LockGuardD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end37
+ 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() {
+ // CHECK-CXX23: void @_ZN7P2718R06test11Ev()
+ // CHECK-CXX23-NEXT: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for (auto e : default_arg_fn2<A>())
+ bar(e);
+}
+
+template <typename T> A foo2(const T&, const DefaultA &Default = DefaultA());
+
+void test12() {
+ // CHECK-CXX23: void @_ZN7P2718R06test12Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R08DefaultAD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for (auto e : default_arg_fn2(foo2(foo2(foo2(A())))))
+ bar(e);
+}
+
+// Test member call && dependent context
+void test13() {
+
+ // CHECK-CXX23: void @_ZN7P2718R06test13Ev()
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R01AD1Ev(
+ // CHECK-CXX23-NEXT: br label %for.end
+ for (auto e : dg<A>().r().g().r().g().r().g())
+ bar(e);
+}
+} // namespace P2718R0
+
More information about the cfe-commits
mailing list