Author: gornishanov Date: Mon May 22 17:33:17 2017 New Revision: 303583 URL: http://llvm.org/viewvc/llvm-project?rev=303583&view=rev Log: [coroutines] Wrap the body of the coroutine in try-catch
Summary: If unhandled_exception member function is present in the coroutine promise, wrap the body of the coroutine in: ``` try { body } catch(...) { promise.unhandled_exception(); } ``` Reviewers: EricWF, rnk, rsmith Reviewed By: rsmith Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D31692 Added: cfe/trunk/test/SemaCXX/coroutine-seh.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/lib/CodeGen/CGCoroutine.cpp cfe/trunk/lib/Sema/SemaCoroutine.cpp cfe/trunk/test/CodeGenCoroutines/coro-cleanup.cpp cfe/trunk/test/CodeGenCoroutines/coro-eh-cleanup.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=303583&r1=303582&r2=303583&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May 22 17:33:17 2017 @@ -8960,6 +8960,8 @@ def warn_coroutine_promise_unhandled_exc InGroup<CoroutineMissingUnhandledException>; def err_coroutine_promise_get_return_object_on_allocation_failure : Error< "%0: 'get_return_object_on_allocation_failure()' must be a static member function">; +def err_seh_in_a_coroutine_with_cxx_exceptions : Error< + "cannot use SEH '__try' in a coroutine when C++ exceptions are enabled">; def err_coroutine_promise_new_requires_nothrow : Error< "%0 is required to have a non-throwing noexcept specification when the promise " "type declares 'get_return_object_on_allocation_failure()'">; Modified: cfe/trunk/lib/CodeGen/CGCoroutine.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCoroutine.cpp?rev=303583&r1=303582&r2=303583&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGCoroutine.cpp (original) +++ cfe/trunk/lib/CodeGen/CGCoroutine.cpp Mon May 22 17:33:17 2017 @@ -270,6 +270,15 @@ struct CallCoroDelete final : public EHS }; } +static void emitBodyAndFallthrough(CodeGenFunction &CGF, + const CoroutineBodyStmt &S, Stmt *Body) { + CGF.EmitStmt(Body); + const bool CanFallthrough = CGF.Builder.GetInsertBlock(); + if (CanFallthrough) + if (Stmt *OnFallthrough = S.getFallthroughHandler()) + CGF.EmitStmt(OnFallthrough); +} + void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { auto *NullPtr = llvm::ConstantPointerNull::get(Builder.getInt8PtrTy()); auto &TI = CGM.getContext().getTargetInfo(); @@ -318,7 +327,19 @@ void CodeGenFunction::EmitCoroutineBody( // FIXME: Emit initial suspend and more before the body. CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal; - EmitStmt(S.getBody()); + + if (auto *OnException = S.getExceptionHandler()) { + auto Loc = S.getLocStart(); + CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr, OnException); + auto *TryStmt = CXXTryStmt::Create(getContext(), Loc, S.getBody(), &Catch); + + EnterCXXTryStmt(*TryStmt); + emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock()); + ExitCXXTryStmt(*TryStmt); + } + else { + emitBodyAndFallthrough(*this, S, S.getBody()); + } // See if we need to generate final suspend. const bool CanFallthrough = Builder.GetInsertBlock(); Modified: cfe/trunk/lib/Sema/SemaCoroutine.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCoroutine.cpp?rev=303583&r1=303582&r2=303583&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaCoroutine.cpp (original) +++ cfe/trunk/lib/Sema/SemaCoroutine.cpp Mon May 22 17:33:17 2017 @@ -1043,6 +1043,15 @@ bool CoroutineStmtBuilder::makeOnExcepti if (UnhandledException.isInvalid()) return false; + // Since the body of the coroutine will be wrapped in try-catch, it will + // be incompatible with SEH __try if present in a function. + if (!S.getLangOpts().Borland && Fn.FirstSEHTryLoc.isValid()) { + S.Diag(Fn.FirstSEHTryLoc, diag::err_seh_in_a_coroutine_with_cxx_exceptions); + S.Diag(Fn.FirstCoroutineStmtLoc, diag::note_declared_coroutine_here) + << Fn.getFirstCoroutineStmtKeyword(); + return false; + } + this->OnException = UnhandledException.get(); return true; } Modified: cfe/trunk/test/CodeGenCoroutines/coro-cleanup.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCoroutines/coro-cleanup.cpp?rev=303583&r1=303582&r2=303583&view=diff ============================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-cleanup.cpp (original) +++ cfe/trunk/test/CodeGenCoroutines/coro-cleanup.cpp Mon May 22 17:33:17 2017 @@ -6,38 +6,38 @@ template <typename... T> struct coroutin template <class Promise = void> struct coroutine_handle { coroutine_handle() = default; - static coroutine_handle from_address(void *) { return {}; } + static coroutine_handle from_address(void *) noexcept; }; template <> struct coroutine_handle<void> { - static coroutine_handle from_address(void *) { return {}; } + static coroutine_handle from_address(void *) noexcept; coroutine_handle() = default; template <class PromiseType> - coroutine_handle(coroutine_handle<PromiseType>) {} + coroutine_handle(coroutine_handle<PromiseType>) noexcept; }; } struct suspend_always { - bool await_ready(); - void await_suspend(std::experimental::coroutine_handle<>); - void await_resume(); + bool await_ready() noexcept; + void await_suspend(std::experimental::coroutine_handle<>) noexcept; + void await_resume() noexcept; }; template <> struct std::experimental::coroutine_traits<void> { struct promise_type { - void get_return_object(); - suspend_always initial_suspend(); - suspend_always final_suspend(); - void return_void(); + void get_return_object() noexcept; + suspend_always initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_void() noexcept; promise_type(); ~promise_type(); - void unhandled_exception(); + void unhandled_exception() noexcept; }; }; struct Cleanup { ~Cleanup(); }; void may_throw(); -// CHECK: define void @_Z1fv( +// CHECK-LABEL: define void @_Z1fv( void f() { // CHECK: call i8* @_Znwm(i64 @@ -52,19 +52,34 @@ void f() { // if may_throw throws, check that we destroy the promise and free the memory. // CHECK: invoke void @_Z9may_throwv( - // CHECK-NEXT: to label %{{.+}} unwind label %[[PromDtorPad:.+]] + // CHECK-NEXT: to label %{{.+}} unwind label %[[CatchPad:.+]] // CHECK: [[DeallocPad]]: // CHECK-NEXT: landingpad // CHECK-NEXT: cleanup // CHECK: br label %[[Dealloc:.+]] - // CHECK: [[PromDtorPad]]: - // CHECK-NEXT: landingpad - // CHECK-NEXT: cleanup - // CHECK: call void @_ZN7CleanupD1Ev(%struct.Cleanup* + // CHECK: [[CatchPad]]: + // CHECK-NEXT: landingpad + // CHECK-NEXT: catch i8* null + // CHECK: call void @_ZN7CleanupD1Ev( + // CHECK: br label %[[Catch:.+]] + + // CHECK: [[Catch]]: + // CHECK: call i8* @__cxa_begin_catch( + // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type19unhandled_exceptionEv( + // CHECK: invoke void @__cxa_end_catch() + // CHECK-NEXT: to label %[[Cont:.+]] unwind + + // CHECK: [[Cont]]: + // CHECK-NEXT: br label %[[Cont2:.+]] + // CHECK: [[Cont2]]: + // CHECK-NEXT: br label %[[Cleanup:.+]] + + // CHECK: [[Cleanup]]: // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeD1Ev( - // CHECK: br label %[[Dealloc]] + // CHECK: %[[Mem0:.+]] = call i8* @llvm.coro.free( + // CHECK: call void @_ZdlPv(i8* %[[Mem0]] // CHECK: [[Dealloc]]: // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free( @@ -72,3 +87,11 @@ void f() { co_return; } + +// CHECK-LABEL: define void @_Z1gv( +void g() { + for (;;) + co_await suspend_always{}; + // Since this is the endless loop there should be no fallthrough handler (call to 'return_void'). + // CHECK-NOT: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv +} Modified: cfe/trunk/test/CodeGenCoroutines/coro-eh-cleanup.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCoroutines/coro-eh-cleanup.cpp?rev=303583&r1=303582&r2=303583&view=diff ============================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-eh-cleanup.cpp (original) +++ cfe/trunk/test/CodeGenCoroutines/coro-eh-cleanup.cpp Mon May 22 17:33:17 2017 @@ -51,7 +51,13 @@ coro_t f() { // CHECK: [[EHCLEANUP]]: // CHECK: %[[INNERPAD:.+]] = cleanuppad within none [] // CHECK: call void @"\01??_DCleanup@@QEAAXXZ"( -// CHECK: cleanupret from %[[INNERPAD]] unwind label %[[COROENDBB:.+]] +// CHECK: cleanupret from %4 unwind label %[[CATCHDISPATCH:.+]] + +// CHECK: [[CATCHDISPATCH]]: +// CHECK: catchswitch within none [label %[[CATCHPAD:.+]]] unwind label %[[COROENDBB:.+]] +// CHECK: [[CATCHPAD]]: +// CHECK: call void @"\01?unhandled_exception@promise_type@coro_t@@QEAAXXZ" + // CHECK: [[COROENDBB]]: // CHECK-NEXT: %[[CLPAD:.+]] = cleanuppad within none // CHECK-NEXT: call i1 @llvm.coro.end(i8* null, i1 true) [ "funclet"(token %[[CLPAD]]) ] @@ -62,8 +68,14 @@ coro_t f() { // CHECK-LPAD: to label %[[CONT:.+]] unwind label %[[EHCLEANUP:.+]] // CHECK-LPAD: [[EHCLEANUP]]: // CHECK-LPAD: landingpad { i8*, i32 } -// CHECK-LPAD: cleanup +// CHECK-LPAD: catch // CHECK-LPAD: call void @_ZN7CleanupD1Ev( +// CHECK-LPAD: call i8* @__cxa_begin_catch +// CHECK-LPAD: call void @_ZN6coro_t12promise_type19unhandled_exceptionEv +// CHECK-LPAD: invoke void @__cxa_end_catch() +// CHECK-LPAD: to label %{{.+}} unwind label %[[UNWINDBB:.+]] + +// CHECK-LPAD: [[UNWINDBB]]: // CHECK-LPAD: %[[I1RESUME:.+]] = call i1 @llvm.coro.end(i8* null, i1 true) // CHECK-LPAD: br i1 %[[I1RESUME]], label %[[EHRESUME:.+]], label // CHECK-LPAD: [[EHRESUME]]: Added: cfe/trunk/test/SemaCXX/coroutine-seh.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/coroutine-seh.cpp?rev=303583&view=auto ============================================================================== --- cfe/trunk/test/SemaCXX/coroutine-seh.cpp (added) +++ cfe/trunk/test/SemaCXX/coroutine-seh.cpp Mon May 22 17:33:17 2017 @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -verify %s -fcxx-exceptions -fexceptions -triple x86_64-windows-msvc -fms-extensions +namespace std::experimental { +template <typename... T> struct coroutine_traits; + +template <class Promise = void> struct coroutine_handle { + coroutine_handle() = default; + static coroutine_handle from_address(void *) noexcept; +}; +template <> struct coroutine_handle<void> { + static coroutine_handle from_address(void *) noexcept; + coroutine_handle() = default; + template <class PromiseType> + coroutine_handle(coroutine_handle<PromiseType>) noexcept; +}; +} + +struct suspend_always { + bool await_ready() noexcept; + void await_suspend(std::experimental::coroutine_handle<>) noexcept; + void await_resume() noexcept; +}; + +template <> struct std::experimental::coroutine_traits<void> { + struct promise_type { + void get_return_object() noexcept; + suspend_always initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_void() noexcept; + void unhandled_exception() noexcept; + }; +}; + +void SEH_used() { + __try { // expected-error {{cannot use SEH '__try' in a coroutine when C++ exceptions are enabled}} + co_return; // expected-note {{function is a coroutine due to use of 'co_return' here}} + } __except(0) {} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits