yronglin updated this revision to Diff 557705. yronglin added a comment. Update
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D153701/new/ https://reviews.llvm.org/D153701 Files: clang/include/clang/Sema/Sema.h clang/lib/Parse/ParseDecl.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaExprCXX.cpp clang/lib/Sema/SemaStmt.cpp clang/test/AST/ast-dump-for-range-lifetime.cpp clang/test/CXX/special/class.temporary/p6.cpp
Index: clang/test/CXX/special/class.temporary/p6.cpp =================================================================== --- clang/test/CXX/special/class.temporary/p6.cpp +++ 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<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 + Index: clang/test/AST/ast-dump-for-range-lifetime.cpp =================================================================== --- /dev/null +++ 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]':'int[3]' lvalue + // CHECK-NEXT: | | `-CallExpr {{.*}} 'int[3]':'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':'const P2718R0::A' lvalue + // CHECK-NEXT: | `-CallExpr {{.*}} 'const P2718R0::A':'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':'const P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'const P2718R0::A &' + // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const P2718R0::A':'const P2718R0::A' <NoOp> + // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'P2718R0::A':'P2718R0::A' (CXXTemporary {{.*}}) + // CHECK-NEXT: | `-CallExpr {{.*}} 'P2718R0::A':'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]':'int[3]' lvalue + // CHECK-NEXT: | `-CallExpr {{.*}} 'int[3]':'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':'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':'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':'const P2718R0::LockGuard' lvalue ',' + // CHECK-NEXT: | | |-CallExpr {{.*}} 'const P2718R0::LockGuard':'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':'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':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&' + // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'P2718R0::A':'P2718R0::A' (CXXTemporary {{.*}}) + // CHECK-NEXT: | `-CallExpr {{.*}} 'P2718R0::A':'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 Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -2736,6 +2736,28 @@ AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild); } +namespace { +struct ForRangeInitTemporaryLifetimeExtensionVisitor + : public RecursiveASTVisitor< + ForRangeInitTemporaryLifetimeExtensionVisitor> { + InitializedEntity &Entity; + ForRangeInitTemporaryLifetimeExtensionVisitor(InitializedEntity &Entity) + : Entity(Entity) {} + bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE) { + MTE->setExtendingDecl(Entity.getDecl(), Entity.allocateManglingNumber()); + return RecursiveASTVisitor< + ForRangeInitTemporaryLifetimeExtensionVisitor>::VisitStmt(MTE); + } + + bool VisitCXXDefaultArgExpr(CXXDefaultArgExpr *D) { + if (D->hasRewrittenInit()) + TraverseStmt(D->getRewrittenExpr()); + return RecursiveASTVisitor< + ForRangeInitTemporaryLifetimeExtensionVisitor>::VisitStmt(D); + } +}; +} // namespace + /// BuildCXXForRangeStmt - Build or instantiate a C++11 for-range statement. StmtResult Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt, @@ -2779,6 +2801,13 @@ LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType())); } } else if (!BeginDeclStmt.get()) { + // P2718R0 - Lifetime extension in range-based for loops. + if (getLangOpts().CPlusPlus23) { + auto Entity = InitializedEntity::InitializeVariable(RangeVar); + ForRangeInitTemporaryLifetimeExtensionVisitor V(Entity); + V.TraverseStmt(RangeVar->getInit()); + } + SourceLocation RangeLoc = RangeVar->getLocation(); const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType(); Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -8151,21 +8151,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 @@ -8186,11 +8171,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; } Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -6218,6 +6218,7 @@ assert(Param->hasDefaultArg() && "can't build nonexistent default arg"); bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); + bool InLifetimeExtendingContext = isInLifetimeExtendingContext(); std::optional<ExpressionEvaluationContextRecord::InitializationContext> InitializationContext = @@ -6251,9 +6252,13 @@ 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}; EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; runWithSufficientStackSpace(CallLoc, [&] { Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -2312,6 +2312,22 @@ 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)) Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -1352,6 +1352,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 @@ -9906,6 +9919,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 ==
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits