yronglin updated this revision to Diff 551851.
yronglin added a comment.
Fix ci
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/SemaInit.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
@@ -27,6 +27,7 @@
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
@@ -2779,6 +2780,17 @@
LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
}
} else if (!BeginDeclStmt.get()) {
+ // P2718R0 - Lifetime extension in range-based for loops.
+ if (getLangOpts().CPlusPlus23) {
+ EnterExpressionEvaluationContext RangeVarContext(
+ *this, ExpressionEvaluationContext::PotentiallyEvaluated);
+ auto &LastRecord = ExprEvalContexts.back();
+ LastRecord.IsCheckingCXXForRangeInitVariable = true;
+ Cleanup = LastRecord.ParentCleanup;
+ auto Entity = InitializedEntity::InitializeVariable(RangeVar);
+ checkInitializerLifetime(Entity, RangeVar->getInit());
+ }
+
SourceLocation RangeLoc = RangeVar->getLocation();
const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType();
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -7357,12 +7357,13 @@
});
}
-static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
+static void visitLocalsRetainedByInitializer(Sema &S, IndirectLocalPath &Path,
Expr *Init, LocalVisitor Visit,
bool RevisitSubinits,
bool EnableLifetimeWarnings);
-static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
+static void visitLocalsRetainedByReferenceBinding(Sema &S,
+ IndirectLocalPath &Path,
Expr *Init, ReferenceKind RK,
LocalVisitor Visit,
bool EnableLifetimeWarnings);
@@ -7448,8 +7449,8 @@
return false;
}
-static void handleGslAnnotatedTypes(IndirectLocalPath &Path, Expr *Call,
- LocalVisitor Visit) {
+static void handleGslAnnotatedTypes(Sema &S, IndirectLocalPath &Path,
+ Expr *Call, LocalVisitor Visit) {
auto VisitPointerArg = [&](const Decl *D, Expr *Arg, bool Value) {
// We are not interested in the temporary base objects of gsl Pointers:
// Temp().ptr; // Here ptr might not dangle.
@@ -7469,11 +7470,11 @@
: IndirectLocalPathEntry::GslReferenceInit,
Arg, D});
if (Arg->isGLValue())
- visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding,
+ visitLocalsRetainedByReferenceBinding(S, Path, Arg, RK_ReferenceBinding,
Visit,
/*EnableLifetimeWarnings=*/true);
else
- visitLocalsRetainedByInitializer(Path, Arg, Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, Arg, Visit, true,
/*EnableLifetimeWarnings=*/true);
Path.pop_back();
};
@@ -7544,8 +7545,8 @@
return false;
}
-static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call,
- LocalVisitor Visit) {
+static void visitLifetimeBoundArguments(Sema &S, IndirectLocalPath &Path,
+ Expr *Call, LocalVisitor Visit) {
const FunctionDecl *Callee;
ArrayRef<Expr*> Args;
@@ -7571,29 +7572,32 @@
auto VisitLifetimeBoundArg = [&](const Decl *D, Expr *Arg) {
Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D});
if (Arg->isGLValue())
- visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding,
+ visitLocalsRetainedByReferenceBinding(S, Path, Arg, RK_ReferenceBinding,
Visit,
/*EnableLifetimeWarnings=*/false);
else
- visitLocalsRetainedByInitializer(Path, Arg, Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, Arg, Visit, true,
/*EnableLifetimeWarnings=*/false);
Path.pop_back();
};
- if (ObjectArg && implicitObjectParamIsLifetimeBound(Callee))
+ if (ObjectArg && (implicitObjectParamIsLifetimeBound(Callee) ||
+ S.isCheckingCXXForRangeInitVariable()))
VisitLifetimeBoundArg(Callee, ObjectArg);
for (unsigned I = 0,
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
I != N; ++I) {
- if (Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
+ if (Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>() ||
+ S.isCheckingCXXForRangeInitVariable())
VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
}
}
/// Visit the locals that would be reachable through a reference bound to the
/// glvalue expression \c Init.
-static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
+static void visitLocalsRetainedByReferenceBinding(Sema &S,
+ IndirectLocalPath &Path,
Expr *Init, ReferenceKind RK,
LocalVisitor Visit,
bool EnableLifetimeWarnings) {
@@ -7615,8 +7619,16 @@
// Step over any subobject adjustments; we may have a materialized
// temporary inside them.
- Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments());
+ SmallVector<const Expr *, 32> CommaLHS;
+ SmallVector<SubobjectAdjustment, 32> Adjustments;
+ Init = const_cast<Expr *>(
+ Init->skipRValueSubobjectAdjustments(CommaLHS, Adjustments));
+ llvm::for_each(CommaLHS, [&](const Expr *LHS) {
+ visitLocalsRetainedByReferenceBinding(S, Path, const_cast<Expr *>(LHS),
+ RK_ReferenceBinding, Visit,
+ EnableLifetimeWarnings);
+ });
// Per current approach for DR1376, look through casts to reference type
// when performing lifetime extension.
if (CastExpr *CE = dyn_cast<CastExpr>(Init))
@@ -7633,7 +7645,7 @@
else
// We can't lifetime extend through this but we might still find some
// retained temporaries.
- return visitLocalsRetainedByInitializer(Path, Init, Visit, true,
+ return visitLocalsRetainedByInitializer(S, Path, Init, Visit, true,
EnableLifetimeWarnings);
}
@@ -7644,18 +7656,24 @@
{IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});
Init = DIE->getExpr();
}
+
+ if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Init)) {
+ Path.push_back(
+ {IndirectLocalPathEntry::DefaultInit, DAE, DAE->getParam()});
+ Init = DAE->getExpr();
+ }
} while (Init != Old);
if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) {
if (Visit(Path, Local(MTE), RK))
- visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, MTE->getSubExpr(), Visit, true,
EnableLifetimeWarnings);
}
if (isa<CallExpr>(Init)) {
if (EnableLifetimeWarnings)
- handleGslAnnotatedTypes(Path, Init, Visit);
- return visitLifetimeBoundArguments(Path, Init, Visit);
+ handleGslAnnotatedTypes(S, Path, Init, Visit);
+ return visitLifetimeBoundArguments(S, Path, Init, Visit);
}
switch (Init->getStmtClass()) {
@@ -7674,7 +7692,7 @@
break;
} else if (VD->getInit() && !isVarOnPath(Path, VD)) {
Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
- visitLocalsRetainedByReferenceBinding(Path, VD->getInit(),
+ visitLocalsRetainedByReferenceBinding(S, Path, VD->getInit(),
RK_ReferenceBinding, Visit,
EnableLifetimeWarnings);
}
@@ -7688,13 +7706,13 @@
// handling all sorts of rvalues passed to a unary operator.
const UnaryOperator *U = cast<UnaryOperator>(Init);
if (U->getOpcode() == UO_Deref)
- visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, U->getSubExpr(), Visit, true,
EnableLifetimeWarnings);
break;
}
case Stmt::OMPArraySectionExprClass: {
- visitLocalsRetainedByInitializer(Path,
+ visitLocalsRetainedByInitializer(S, Path,
cast<OMPArraySectionExpr>(Init)->getBase(),
Visit, true, EnableLifetimeWarnings);
break;
@@ -7704,11 +7722,11 @@
case Stmt::BinaryConditionalOperatorClass: {
auto *C = cast<AbstractConditionalOperator>(Init);
if (!C->getTrueExpr()->getType()->isVoidType())
- visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit,
- EnableLifetimeWarnings);
+ visitLocalsRetainedByReferenceBinding(S, Path, C->getTrueExpr(), RK,
+ Visit, EnableLifetimeWarnings);
if (!C->getFalseExpr()->getType()->isVoidType())
- visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit,
- EnableLifetimeWarnings);
+ visitLocalsRetainedByReferenceBinding(S, Path, C->getFalseExpr(), RK,
+ Visit, EnableLifetimeWarnings);
break;
}
@@ -7721,7 +7739,7 @@
/// Visit the locals that would be reachable through an object initialized by
/// the prvalue expression \c Init.
-static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
+static void visitLocalsRetainedByInitializer(Sema &S, IndirectLocalPath &Path,
Expr *Init, LocalVisitor Visit,
bool RevisitSubinits,
bool EnableLifetimeWarnings) {
@@ -7757,23 +7775,26 @@
// initializer.
Path.push_back({IndirectLocalPathEntry::LValToRVal, CE});
return visitLocalsRetainedByReferenceBinding(
- Path, Init, RK_ReferenceBinding,
+ S, Path, Init, RK_ReferenceBinding,
[&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool {
- if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
- auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
- if (VD && VD->getType().isConstQualified() && VD->getInit() &&
- !isVarOnPath(Path, VD)) {
- Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
- visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit, true,
- EnableLifetimeWarnings);
- }
- } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
- if (MTE->getType().isConstQualified())
- visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit,
- true, EnableLifetimeWarnings);
- }
- return false;
- }, EnableLifetimeWarnings);
+ if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
+ auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ if (VD && VD->getType().isConstQualified() && VD->getInit() &&
+ !isVarOnPath(Path, VD)) {
+ Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});
+ visitLocalsRetainedByInitializer(S, Path, VD->getInit(),
+ Visit, true,
+ EnableLifetimeWarnings);
+ }
+ } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
+ if (MTE->getType().isConstQualified())
+ visitLocalsRetainedByInitializer(S, Path, MTE->getSubExpr(),
+ Visit, true,
+ EnableLifetimeWarnings);
+ }
+ return false;
+ },
+ EnableLifetimeWarnings);
// We assume that objects can be retained by pointers cast to integers,
// but not if the integer is cast to floating-point type or to _Complex.
@@ -7802,7 +7823,7 @@
// Model array-to-pointer decay as taking the address of the array
// lvalue.
Path.push_back({IndirectLocalPathEntry::AddressOf, CE});
- return visitLocalsRetainedByReferenceBinding(Path, CE->getSubExpr(),
+ return visitLocalsRetainedByReferenceBinding(S, Path, CE->getSubExpr(),
RK_ReferenceBinding, Visit,
EnableLifetimeWarnings);
@@ -7818,7 +7839,7 @@
// initializing an initializer_list object from the array extends the
// lifetime of the array exactly like binding a reference to a temporary.
if (auto *ILE = dyn_cast<CXXStdInitializerListExpr>(Init))
- return visitLocalsRetainedByReferenceBinding(Path, ILE->getSubExpr(),
+ return visitLocalsRetainedByReferenceBinding(S, Path, ILE->getSubExpr(),
RK_StdInitializerList, Visit,
EnableLifetimeWarnings);
@@ -7830,13 +7851,13 @@
return;
if (ILE->isTransparent())
- return visitLocalsRetainedByInitializer(Path, ILE->getInit(0), Visit,
+ return visitLocalsRetainedByInitializer(S, Path, ILE->getInit(0), Visit,
RevisitSubinits,
EnableLifetimeWarnings);
if (ILE->getType()->isArrayType()) {
for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I)
- visitLocalsRetainedByInitializer(Path, ILE->getInit(I), Visit,
+ visitLocalsRetainedByInitializer(S, Path, ILE->getInit(I), Visit,
RevisitSubinits,
EnableLifetimeWarnings);
return;
@@ -7850,13 +7871,13 @@
// bound to temporaries, those temporaries are also lifetime-extended.
if (RD->isUnion() && ILE->getInitializedFieldInUnion() &&
ILE->getInitializedFieldInUnion()->getType()->isReferenceType())
- visitLocalsRetainedByReferenceBinding(Path, ILE->getInit(0),
+ visitLocalsRetainedByReferenceBinding(S, Path, ILE->getInit(0),
RK_ReferenceBinding, Visit,
EnableLifetimeWarnings);
else {
unsigned Index = 0;
for (; Index < RD->getNumBases() && Index < ILE->getNumInits(); ++Index)
- visitLocalsRetainedByInitializer(Path, ILE->getInit(Index), Visit,
+ visitLocalsRetainedByInitializer(S, Path, ILE->getInit(Index), Visit,
RevisitSubinits,
EnableLifetimeWarnings);
for (const auto *I : RD->fields()) {
@@ -7866,14 +7887,14 @@
continue;
Expr *SubInit = ILE->getInit(Index);
if (I->getType()->isReferenceType())
- visitLocalsRetainedByReferenceBinding(Path, SubInit,
+ visitLocalsRetainedByReferenceBinding(S, Path, SubInit,
RK_ReferenceBinding, Visit,
EnableLifetimeWarnings);
else
// This might be either aggregate-initialization of a member or
// initialization of a std::initializer_list object. Regardless,
// we should recursively lifetime-extend that initializer.
- visitLocalsRetainedByInitializer(Path, SubInit, Visit,
+ visitLocalsRetainedByInitializer(S, Path, SubInit, Visit,
RevisitSubinits,
EnableLifetimeWarnings);
++Index;
@@ -7895,10 +7916,10 @@
if (Cap.capturesVariable())
Path.push_back({IndirectLocalPathEntry::LambdaCaptureInit, E, &Cap});
if (E->isGLValue())
- visitLocalsRetainedByReferenceBinding(Path, E, RK_ReferenceBinding,
+ visitLocalsRetainedByReferenceBinding(S, Path, E, RK_ReferenceBinding,
Visit, EnableLifetimeWarnings);
else
- visitLocalsRetainedByInitializer(Path, E, Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, E, Visit, true,
EnableLifetimeWarnings);
if (Cap.capturesVariable())
Path.pop_back();
@@ -7913,8 +7934,8 @@
Expr *Arg = MTE->getSubExpr();
Path.push_back({IndirectLocalPathEntry::TemporaryCopy, Arg,
CCE->getConstructor()});
- visitLocalsRetainedByInitializer(Path, Arg, Visit, true,
- /*EnableLifetimeWarnings*/false);
+ visitLocalsRetainedByInitializer(S, Path, Arg, Visit, true,
+ /*EnableLifetimeWarnings*/ false);
Path.pop_back();
}
}
@@ -7922,8 +7943,8 @@
if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init)) {
if (EnableLifetimeWarnings)
- handleGslAnnotatedTypes(Path, Init, Visit);
- return visitLifetimeBoundArguments(Path, Init, Visit);
+ handleGslAnnotatedTypes(S, Path, Init, Visit);
+ return visitLifetimeBoundArguments(S, Path, Init, Visit);
}
switch (Init->getStmtClass()) {
@@ -7939,7 +7960,7 @@
return;
Path.push_back({IndirectLocalPathEntry::AddressOf, UO});
- visitLocalsRetainedByReferenceBinding(Path, UO->getSubExpr(),
+ visitLocalsRetainedByReferenceBinding(S, Path, UO->getSubExpr(),
RK_ReferenceBinding, Visit,
EnableLifetimeWarnings);
}
@@ -7954,10 +7975,10 @@
break;
if (BO->getLHS()->getType()->isPointerType())
- visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, BO->getLHS(), Visit, true,
EnableLifetimeWarnings);
else if (BO->getRHS()->getType()->isPointerType())
- visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, BO->getRHS(), Visit, true,
EnableLifetimeWarnings);
break;
}
@@ -7968,10 +7989,10 @@
// In C++, we can have a throw-expression operand, which has 'void' type
// and isn't interesting from a lifetime perspective.
if (!C->getTrueExpr()->getType()->isVoidType())
- visitLocalsRetainedByInitializer(Path, C->getTrueExpr(), Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, C->getTrueExpr(), Visit, true,
EnableLifetimeWarnings);
if (!C->getFalseExpr()->getType()->isVoidType())
- visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true,
+ visitLocalsRetainedByInitializer(S, Path, C->getFalseExpr(), Visit, true,
EnableLifetimeWarnings);
break;
}
@@ -8125,6 +8146,12 @@
return false;
}
+ if (isCheckingCXXForRangeInitVariable()) {
+ MTE->setExtendingDecl(ExtendingEntity->getDecl(),
+ ExtendingEntity->allocateManglingNumber());
+ return true;
+ }
+
switch (shouldLifetimeExtendThroughPath(Path)) {
case PathLifetimeKind::Extend:
// Update the storage duration of the materialized temporary.
@@ -8330,11 +8357,11 @@
diag::warn_dangling_lifetime_pointer, SourceLocation());
llvm::SmallVector<IndirectLocalPathEntry, 8> Path;
if (Init->isGLValue())
- visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,
- TemporaryVisitor,
+ visitLocalsRetainedByReferenceBinding(*this, Path, Init,
+ RK_ReferenceBinding, TemporaryVisitor,
EnableLifetimeWarnings);
else
- visitLocalsRetainedByInitializer(Path, Init, TemporaryVisitor, false,
+ visitLocalsRetainedByInitializer(*this, Path, Init, TemporaryVisitor, false,
EnableLifetimeWarnings);
}
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -8183,21 +8183,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
@@ -8218,11 +8203,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 temporay 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 (isMaterializePRValueInDiscardStatement() && 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
@@ -6240,9 +6240,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 || isRewriteDefaultArgument()) {
+ if (V.HasImmediateCalls)
+ ExprEvalContexts.back().DelayedDefaultInitializationContext = {
+ CallLoc, Param, CurContext};
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;
runWithSufficientStackSpace(CallLoc, [&] {
@@ -18165,7 +18169,8 @@
ExprEvalContexts.back().InImmediateEscalatingFunctionContext =
Prev.InImmediateEscalatingFunctionContext;
-
+ ExprEvalContexts.back().MaterializePRValueInDiscardStatement =
+ Prev.MaterializePRValueInDiscardStatement;
Cleanup.reset();
if (!MaybeODRUseExprs.empty())
std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs);
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -2323,6 +2323,26 @@
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();
+
+ // Rewrite the default argument, then we can extend the lifetime of
+ // temporaries inside default argument.
+ LastRecord.IsRewriteDefaultArgument = true;
+
+ // Materialize non-`cv void` prvalue temporaries in discard statement
+ // during parsing. These materialized temporaries may be extented
+ // lifetime.
+ LastRecord.MaterializePRValueInDiscardStatement = 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
@@ -1353,6 +1353,23 @@
bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false;
+ /// Whether rewrite the default argument.
+ bool IsRewriteDefaultArgument = false;
+
+ /// Whether we are currently checking C++ for-range-init variable.
+ /// Eg. Extending the lifetime of temporaries in the init expression.
+ bool IsCheckingCXXForRangeInitVariable = false;
+
+ /// Whether we should materialize temporary the non `cv void` prvalue in
+ /// discard statement.
+ ///
+ /// [6.7.7.2.6] when a prvalue that has type other than cv void appears as a
+ /// discarded-value expression ([expr.context]).
+ ///
+ /// We do not materialize temporay by default in order to avoid creating
+ /// unnecessary temporary objects.
+ bool MaterializePRValueInDiscardStatement = 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
@@ -9834,6 +9851,24 @@
return ExprEvalContexts.back().isImmediateFunctionContext();
}
+ bool isRewriteDefaultArgument() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().IsRewriteDefaultArgument;
+ }
+
+ bool isCheckingCXXForRangeInitVariable() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().IsCheckingCXXForRangeInitVariable;
+ }
+
+ bool isMaterializePRValueInDiscardStatement() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().MaterializePRValueInDiscardStatement;
+ }
+
bool isCheckingDefaultArgumentOrInitializer() const {
assert(!ExprEvalContexts.empty() &&
"Must be in an expression evaluation context");
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits