[clang] e4d178a - [clang][Serialization] Don't duplicate the body of LambdaExpr during deserialization
Bruno Ricci via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 2 06:17:16 PDT 2020
Author: Bruno Ricci
Date: 2020-07-02T14:13:35+01:00
New Revision: e4d178a752444453f0ab8d2b9085087208aa8296
URL: https://github.com/llvm/llvm-project/commit/e4d178a752444453f0ab8d2b9085087208aa8296
DIFF: https://github.com/llvm/llvm-project/commit/e4d178a752444453f0ab8d2b9085087208aa8296.diff
LOG: [clang][Serialization] Don't duplicate the body of LambdaExpr during deserialization
05843dc6ab97a00cbde7aa4f08bf3692eb83109d changed the serialization of the body
of LambdaExpr to avoid a mutation in LambdaExpr::getBody and to avoid a missing
body in LambdaExpr::children.
Unfortunately this replaced one bug by another: we are now duplicating the body
during deserialization; that is after deserialization the identity:
E->getBody() == E->getCallOperator()->getBody() does not hold.
Fix that by instead lazily loading the body from the call operator when needed.
Differential Revision: https://reviews.llvm.org/D83009
Reviewed By: martong, aaron.ballman, vabridgers
Added:
clang/test/AST/ast-dump-lambda-body-not-duplicated.cpp
Modified:
clang/include/clang/AST/ExprCXX.h
clang/lib/AST/ExprCXX.cpp
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 9906cd9df074..178f4db77061 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -1852,6 +1852,8 @@ class LambdaExpr final : public Expr,
Stmt **getStoredStmts() { return getTrailingObjects<Stmt *>(); }
Stmt *const *getStoredStmts() const { return getTrailingObjects<Stmt *>(); }
+ void initBodyIfNeeded() const;
+
public:
friend class ASTStmtReader;
friend class ASTStmtWriter;
@@ -2000,17 +2002,12 @@ class LambdaExpr final : public Expr,
/// a \p CompoundStmt, but can also be \p CoroutineBodyStmt wrapping
/// a \p CompoundStmt. Note that unlike functions, lambda-expressions
/// cannot have a function-try-block.
- Stmt *getBody() const { return getStoredStmts()[capture_size()]; }
+ Stmt *getBody() const;
/// Retrieve the \p CompoundStmt representing the body of the lambda.
/// This is a convenience function for callers who do not need
/// to handle node(s) which may wrap a \p CompoundStmt.
- const CompoundStmt *getCompoundStmtBody() const {
- Stmt *Body = getBody();
- if (const auto *CoroBody = dyn_cast<CoroutineBodyStmt>(Body))
- return cast<CompoundStmt>(CoroBody->getBody());
- return cast<CompoundStmt>(Body);
- }
+ const CompoundStmt *getCompoundStmtBody() const;
CompoundStmt *getCompoundStmtBody() {
const auto *ConstThis = this;
return const_cast<CompoundStmt *>(ConstThis->getCompoundStmtBody());
@@ -2039,15 +2036,9 @@ class LambdaExpr final : public Expr,
SourceLocation getEndLoc() const LLVM_READONLY { return ClosingBrace; }
- child_range children() {
- // Includes initialization exprs plus body stmt
- return child_range(getStoredStmts(), getStoredStmts() + capture_size() + 1);
- }
-
- const_child_range children() const {
- return const_child_range(getStoredStmts(),
- getStoredStmts() + capture_size() + 1);
- }
+ /// Includes the captures and the body of the lambda.
+ child_range children();
+ const_child_range children() const;
};
/// An expression "T()" which creates a value-initialized rvalue of type
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 2889604308dd..5d99f61c579f 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1118,6 +1118,10 @@ LambdaExpr::LambdaExpr(QualType T, SourceRange IntroducerRange,
LambdaExpr::LambdaExpr(EmptyShell Empty, unsigned NumCaptures)
: Expr(LambdaExprClass, Empty) {
LambdaExprBits.NumCaptures = NumCaptures;
+
+ // Initially don't initialize the body of the LambdaExpr. The body will
+ // be lazily deserialized when needed.
+ getStoredStmts()[NumCaptures] = nullptr; // Not one past the end.
}
LambdaExpr *LambdaExpr::Create(const ASTContext &Context, CXXRecordDecl *Class,
@@ -1147,6 +1151,25 @@ LambdaExpr *LambdaExpr::CreateDeserialized(const ASTContext &C,
return new (Mem) LambdaExpr(EmptyShell(), NumCaptures);
}
+void LambdaExpr::initBodyIfNeeded() const {
+ if (!getStoredStmts()[capture_size()]) {
+ auto *This = const_cast<LambdaExpr *>(this);
+ This->getStoredStmts()[capture_size()] = getCallOperator()->getBody();
+ }
+}
+
+Stmt *LambdaExpr::getBody() const {
+ initBodyIfNeeded();
+ return getStoredStmts()[capture_size()];
+}
+
+const CompoundStmt *LambdaExpr::getCompoundStmtBody() const {
+ Stmt *Body = getBody();
+ if (const auto *CoroBody = dyn_cast<CoroutineBodyStmt>(Body))
+ return cast<CompoundStmt>(CoroBody->getBody());
+ return cast<CompoundStmt>(Body);
+}
+
bool LambdaExpr::isInitCapture(const LambdaCapture *C) const {
return (C->capturesVariable() && C->getCapturedVar()->isInitCapture() &&
(getCallOperator() == C->getCapturedVar()->getDeclContext()));
@@ -1216,6 +1239,17 @@ ArrayRef<NamedDecl *> LambdaExpr::getExplicitTemplateParameters() const {
bool LambdaExpr::isMutable() const { return !getCallOperator()->isConst(); }
+LambdaExpr::child_range LambdaExpr::children() {
+ initBodyIfNeeded();
+ return child_range(getStoredStmts(), getStoredStmts() + capture_size() + 1);
+}
+
+LambdaExpr::const_child_range LambdaExpr::children() const {
+ initBodyIfNeeded();
+ return const_child_range(getStoredStmts(),
+ getStoredStmts() + capture_size() + 1);
+}
+
ExprWithCleanups::ExprWithCleanups(Expr *subexpr,
bool CleanupsHaveSideEffects,
ArrayRef<CleanupObject> objects)
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 9d4a0b6ccafd..9a73ce1f9b2e 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1715,12 +1715,12 @@ void ASTStmtReader::VisitLambdaExpr(LambdaExpr *E) {
// Read capture initializers.
for (LambdaExpr::capture_init_iterator C = E->capture_init_begin(),
- CEnd = E->capture_init_end();
+ CEnd = E->capture_init_end();
C != CEnd; ++C)
*C = Record.readSubExpr();
- // Ok, not one past the end.
- E->getStoredStmts()[NumCaptures] = Record.readSubStmt();
+ // The body will be lazily deserialized when needed from the call operator
+ // declaration.
}
void
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index abc6081da502..71c2e9e29757 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1615,7 +1615,8 @@ void ASTStmtWriter::VisitLambdaExpr(LambdaExpr *E) {
Record.AddStmt(*C);
}
- Record.AddStmt(E->getBody());
+ // Don't serialize the body. It belongs to the call operator declaration.
+ // LambdaExpr only stores a copy of the Stmt *.
Code = serialization::EXPR_LAMBDA;
}
diff --git a/clang/test/AST/ast-dump-lambda-body-not-duplicated.cpp b/clang/test/AST/ast-dump-lambda-body-not-duplicated.cpp
new file mode 100644
index 000000000000..558455e239c7
--- /dev/null
+++ b/clang/test/AST/ast-dump-lambda-body-not-duplicated.cpp
@@ -0,0 +1,40 @@
+// Test without serialization:
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-unused-value -ast-dump %s \
+// RUN: | FileCheck %s
+//
+// Test with serialization:
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-unused-value -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -triple x86_64-unknown-unknown -Wno-unused-value \
+// RUN: -include-pch %t -ast-dump-all /dev/null \
+// RUN: | FileCheck %s
+
+// Make sure that the Stmt * for the body of the LambdaExpr is
+// equal to the Stmt * for the body of the call operator.
+void Test0() {
+ []() {
+ return 42;
+ };
+}
+
+// CHECK: FunctionDecl {{.*}} Test0
+//
+// CHECK: CXXMethodDecl {{.*}} operator() 'int () const' inline
+// CHECK-NEXT: CompoundStmt 0x[[TMP0:.*]]
+// CHECK: IntegerLiteral {{.*}} 'int' 42
+//
+// CHECK: CompoundStmt 0x[[TMP0]]
+// Check: IntegerLiteral {{.*}} 'int' 42
+
+void Test1() {
+ [](auto x) { return x; };
+}
+
+// CHECK: FunctionDecl {{.*}} Test1
+//
+// CHECK: CXXMethodDecl {{.*}} operator() 'auto (auto) const' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} referenced x 'auto'
+// CHECK-NEXT: CompoundStmt 0x[[TMP1:.*]]
+// CHECK: DeclRefExpr {{.*}} 'x' 'auto'
+//
+// CHECK: CompoundStmt 0x[[TMP1]]
+// CHECK: DeclRefExpr {{.*}} 'x' 'auto'
More information about the cfe-commits
mailing list