Fix an assertion when ending a function definition.
The bug is that ExprCleanupObjects isn't always empty
in a fresh evaluation context. New evaluation contexts just
track the current depth of the stack.
The assertion will misfire whenever we finish processing
a function body inside an expression that contained an earlier
block literal with non-trivial captures. That's actually
a lot less likely than you'd think, though, because it has
to be a real function declaration, not just another block.
Mixed block/lambda code would work, as would a template
instantiation or a local class definition.
The code works correctly if the assertion is disabled.
rdar://16356628
llvm-svn: 222194
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a4b995f..b63863e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10579,7 +10579,8 @@
}
}
- assert(ExprCleanupObjects.empty() && "Leftover temporaries in function");
+ assert(ExprCleanupObjects.size() == ExprEvalContexts.back().NumCleanupObjects
+ && "Leftover temporaries in function");
assert(!ExprNeedsCleanups && "Unaccounted cleanups in function");
assert(MaybeODRUseExprs.empty() &&
"Leftover expressions for odr-use checking");
diff --git a/clang/test/SemaCXX/blocks.cpp b/clang/test/SemaCXX/blocks.cpp
index a2672d1..0521802 100644
--- a/clang/test/SemaCXX/blocks.cpp
+++ b/clang/test/SemaCXX/blocks.cpp
@@ -100,3 +100,48 @@
});
}
}
+
+
+// rdar://16356628
+//
+// Ensure that we can end function bodies while parsing an
+// expression that requires an explicitly-tracked cleanup object
+// (i.e. a block literal).
+
+// The nested function body in this test case is a template
+// instantiation. The template function has to be constexpr because
+// we'll otherwise delay its instantiation to the end of the
+// translation unit.
+namespace test6a {
+ template <class T> constexpr int func() { return 0; }
+ void run(void (^)(), int);
+
+ void test() {
+ int aCapturedVar = 0;
+ run(^{ (void) aCapturedVar; }, func<int>());
+ }
+}
+
+// The nested function body in this test case is a method of a local
+// class.
+namespace test6b {
+ void run(void (^)(), void (^)());
+ void test() {
+ int aCapturedVar = 0;
+ run(^{ (void) aCapturedVar; },
+ ^{ struct A { static void foo() {} };
+ A::foo(); });
+ }
+}
+
+// The nested function body in this test case is a lambda invocation
+// function.
+namespace test6c {
+ void run(void (^)(), void (^)());
+ void test() {
+ int aCapturedVar = 0;
+ run(^{ (void) aCapturedVar; },
+ ^{ struct A { static void foo() {} };
+ A::foo(); });
+ }
+}