From e89e1418ad1e8c62bd3609e8da2d5b07a12e6af3 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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@222194 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDecl.cpp | 3 ++- test/SemaCXX/blocks.cpp | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index a4b995fa97..b63863e96b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/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/test/SemaCXX/blocks.cpp b/test/SemaCXX/blocks.cpp index a2672d13b7..0521802172 100644 --- a/test/SemaCXX/blocks.cpp +++ b/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.40.0