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

Reply via email to