diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2373,7 +2373,7 @@ struct ForRangeInit { SourceLocation ColonLoc; ExprResult RangeExpr; - + SmallVector 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 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1338,6 +1338,12 @@ /// context not already known to be immediately invoked. llvm::SmallPtrSet 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 ForRangeLifetimeExtendTemps; + /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { @@ -1357,6 +1363,19 @@ // 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 @@ -5203,7 +5222,8 @@ Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection, SourceLocation RParenLoc, - BuildForRangeKind Kind); + BuildForRangeKind Kind, + ArrayRef LifetimeExtendTemps = {}); StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt, @@ -9943,6 +9963,18 @@ 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 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2312,12 +2312,31 @@ 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 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -2277,7 +2277,7 @@ 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. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6247,7 +6247,7 @@ assert(Param->hasDefaultArg() && "can't build nonexistent default arg"); bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - + bool IsInLifetimeExtendingContext = isInLifetimeExtendingContext(); std::optional InitializationContext = OutermostDeclarationWithDelayedImmediateInvocations(); @@ -6280,9 +6280,19 @@ 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, [&] { @@ -6296,6 +6306,10 @@ 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 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8209,21 +8209,6 @@ 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,32 @@ // 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 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -8471,6 +8471,10 @@ // 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 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2483,7 +2483,8 @@ SourceLocation CoawaitLoc, Stmt *InitStmt, Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc, - BuildForRangeKind Kind) { + BuildForRangeKind Kind, + ArrayRef LifetimeExtendTemps) { // FIXME: recover in order to allow the body to be parsed. if (!First) return StmtError(); @@ -2535,6 +2536,12 @@ 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 **)&RangeVar, 1)); @@ -2779,6 +2786,7 @@ 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 --- /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: | |-<<>> + // 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 &)' + // 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' + // CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}}) + // CHECK-NEXT: | | `-CallExpr {{.*}} 'A':'P2718R0::A' + // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'A (*)()' + // 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: | |-<<>> + // 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]' + // 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 &)' + // CHECK-NEXT: | | | `-DeclRefExpr {{.*}} 'const A *(const A &)' lvalue Function {{.*}} 'g' 'const A *(const A &)' + // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const A':'const P2718R0::A' lvalue + // CHECK-NEXT: | | `-MaterializeTemporaryExpr {{.*}} 'const B':'const P2718R0::B' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]' + // CHECK-NEXT: | | `-ImplicitCastExpr {{.*}} 'const B':'const P2718R0::B' + // 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: |-<<>> + // 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 + // 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(LockGuard()), v) + LockGuard guard; + + // CHECK: -CXXForRangeStmt {{.*}} + // CHECK-NEXT: |-<<>> + // 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' + // 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: |-<<>> + // 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: |-<<>> + // 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 {{.*}} '' .g {{.*}} + // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue + // CHECK-NEXT: | `-MemberExpr {{.*}} '' .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 {{.*}} '' .g {{.*}} + // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue + // CHECK-NEXT: | `-MemberExpr {{.*}} '' .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 {{.*}} '' .g {{.*}} + // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue + // CHECK-NEXT: | `-MemberExpr {{.*}} '' .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 (*)()' + // 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 T dg() { return T(); } +template const T &df1(const T &t) { return t; } + +void test8() { + [[maybe_unused]] int sum = 0; + // CHECK: FunctionDecl {{.*}} test8 'void ()' + // CHECK: -CXXForRangeStmt {{.*}} + // CHECK-NEXT: |-<<>> + // 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 &)' + // 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' + // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'P2718R0::A' (CXXTemporary {{.*}}) + // CHECK-NEXT: | `-CallExpr {{.*}} 'P2718R0::A' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'P2718R0::A (*)()' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'P2718R0::A ()' lvalue Function {{.*}} 'dg' 'P2718R0::A ()' (FunctionTemplate {{.*}} 'dg') + for (auto e : df1(dg())) + sum += e; +} + +template int (&df2(const T *))[3]; +const A *dg2(const A &); + +void test9() { + // CHECK: FunctionDecl {{.*}} test9 'void ()' + // CHECK: -CXXForRangeStmt {{.*}} + // CHECK-NEXT: |-<<>> + // 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]' + // 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 &)' + // CHECK-NEXT: | | `-DeclRefExpr {{.*}} 'const A *(const A &)' lvalue Function {{.*}} 'dg2' 'const A *(const A &)' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const A':'const P2718R0::A' lvalue + // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const B':'const P2718R0::B' lvalue extended by Var {{.*}} '__range1' 'int (&)[3]' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const B':'const P2718R0::B' + // 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: |-<<>> + // 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 + // CHECK-NEXT: | | `-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue + // CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' + // 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' + // 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(df1(LockGuard())), v) + LockGuard guard; + + // CHECK: -CXXForRangeStmt {{.*}} + // CHECK-NEXT: |-<<>> + // 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' + // CHECK-NEXT: | | `-CallExpr {{.*}} 'const P2718R0::LockGuard' lvalue + // CHECK-NEXT: | | |-ImplicitCastExpr {{.*}} 'const P2718R0::LockGuard &(*)(const P2718R0::LockGuard &)' + // 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' + // 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: |-<<>> + // 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 &)' + // 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' + // 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 &)' + // 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' + // 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 int (&default_arg_fn2(const T & = T()))[3]; +void test11() { + for (auto e : default_arg_fn2()) + bar(e); +} + +template 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: |-<<>> + // 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 {{.*}} '' .g {{.*}} + // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue + // CHECK-NEXT: | `-MemberExpr {{.*}} '' .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 {{.*}} '' .g {{.*}} + // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue + // CHECK-NEXT: | `-MemberExpr {{.*}} '' .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 {{.*}} '' .g {{.*}} + // CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue + // CHECK-NEXT: | `-MemberExpr {{.*}} '' .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 (*)()' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'P2718R0::A ()' lvalue Function {{.*}} 'dg' 'P2718R0::A ()' (FunctionTemplate {{.*}} 'dg') + for (auto e : dg().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 --- 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 @@ // 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(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 T dg() { return T(); } +template 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())) + sum += e; +} + +template 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(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 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()) + bar(e); +} + +template 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().r().g().r().g().r().g()) + bar(e); +} +} // namespace P2718R0 +