Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -8960,6 +8960,8 @@ InGroup; 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()'">; Index: cfe/trunk/lib/CodeGen/CGCoroutine.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGCoroutine.cpp +++ cfe/trunk/lib/CodeGen/CGCoroutine.cpp @@ -270,6 +270,15 @@ }; } +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 @@ // 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(); Index: cfe/trunk/lib/Sema/SemaCoroutine.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaCoroutine.cpp +++ cfe/trunk/lib/Sema/SemaCoroutine.cpp @@ -1043,6 +1043,15 @@ 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; } Index: cfe/trunk/test/CodeGenCoroutines/coro-cleanup.cpp =================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-cleanup.cpp +++ cfe/trunk/test/CodeGenCoroutines/coro-cleanup.cpp @@ -6,38 +6,38 @@ template struct coroutine_handle { coroutine_handle() = default; - static coroutine_handle from_address(void *) { return {}; } + static coroutine_handle from_address(void *) noexcept; }; template <> struct coroutine_handle { - static coroutine_handle from_address(void *) { return {}; } + static coroutine_handle from_address(void *) noexcept; coroutine_handle() = default; template - coroutine_handle(coroutine_handle) {} + coroutine_handle(coroutine_handle) 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 { 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 @@ // 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 @@ 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 +} Index: cfe/trunk/test/CodeGenCoroutines/coro-eh-cleanup.cpp =================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-eh-cleanup.cpp +++ cfe/trunk/test/CodeGenCoroutines/coro-eh-cleanup.cpp @@ -51,7 +51,13 @@ // 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 @@ // 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]]: Index: cfe/trunk/test/SemaCXX/coroutine-seh.cpp =================================================================== --- cfe/trunk/test/SemaCXX/coroutine-seh.cpp +++ cfe/trunk/test/SemaCXX/coroutine-seh.cpp @@ -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 struct coroutine_traits; + +template struct coroutine_handle { + coroutine_handle() = default; + static coroutine_handle from_address(void *) noexcept; +}; +template <> struct coroutine_handle { + static coroutine_handle from_address(void *) noexcept; + coroutine_handle() = default; + template + coroutine_handle(coroutine_handle) 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 { + 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) {} +}