From 0d8d6c094f5da64e2c1bda992e8a65a5af27fdb1 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 18 Nov 2014 00:19:01 +0000 Subject: [PATCH] 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 --- clang/lib/Sema/SemaDecl.cpp | 3 ++- clang/test/SemaCXX/blocks.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) 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 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, } } - 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 @@ namespace test5 { }); } } + + +// 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 constexpr int func() { return 0; } + void run(void (^)(), int); + + void test() { + int aCapturedVar = 0; + run(^{ (void) aCapturedVar; }, func()); + } +} + +// 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(); }); + } +} -- 2.7.4