Skip to content

Commit c0a7391

Browse files
committed
[ItaniumCXXABI] Add -fassume-nothrow-exception-dtor to assume that all exception objects' destructors are non-throwing
Link: https://lists.llvm.org/pipermail/cfe-dev/2021-August/068740.html ("[Exception Handling] Could we mark __cxa_end_catch as nounwind conditionally?" Link: #57375 A catch handler calls `__cxa_begin_catch` and `__cxa_end_catch`. For a catch-all clause or a catch clause matching a record type, we: * assume that the exception object may have a throwing destructor * emit `invoke void @__cxa_end_catch` (as the call is not marked as the `nounwind` attribute). * emit a landing pad to destroy local variables and call `_Unwind_Resume` ``` struct A { ~A(); }; struct B { int x; }; void opaque(); void foo() { A a; try { opaque(); } catch (...) { } // the exception object has an unknown type and may throw try { opaque(); } catch (B b) { } // B::~B is nothrow, but we do not utilize this } ``` Per C++ [dcl.fct.def.coroutine], a coroutine's function body implies a `catch (...)`. Our code generation pessimizes even simple code, like: ``` UserFacing foo() { A a; opaque(); co_return; // For `invoke void @__cxa_end_catch()`, the landing pad destroys the // promise_type and deletes the coro frame. } ``` Throwing destructors are typically discouraged. In many environments, the destructors of exception objects are guaranteed to never throw, making our conservative code generation approach seem wasteful. Furthermore, throwing destructors tend not to work well in practice: * GCC does not emit call site records for the region containing `__cxa_end_catch`. This has been a long time, since 2000. * If a catch-all clause catches an exception object that throws, both GCC and Clang using libstdc++ leak the allocated exception object. To avoid code generation pessimization, add an opt-in driver option -fassume-nothrow-exception-dtor to assume that `__cxa_end_catch` calls have the `nounwind` attribute. This implies that thrown exception objects' destructors will never throw. To detect misuses, diagnose throw expressions with a potentially-throwing destructor. Technically, it is possible that a potentially-throwing destructor never throws when called transitively by `__cxa_end_catch`, but these cases seem rare enough to justify a relaxed mode. Reviewed By: ChuanqiXu Differential Revision: https://reviews.llvm.org/D108905
1 parent a9af8f8 commit c0a7391

File tree

13 files changed

+134
-53
lines changed

13 files changed

+134
-53
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ New Compiler Flags
232232
preserving ``#include`` directives for "system" headers instead of copying
233233
the preprocessed text to the output. This can greatly reduce the size of the
234234
preprocessed output, which can be helpful when trying to reduce a test case.
235+
* ``-fassume-nothrow-exception-dtor`` is added to assume that the destructor of
236+
an thrown exception object will not throw. The generated code for catch
237+
handlers will be smaller. A throw expression of a type with a
238+
potentially-throwing destructor will lead to an error.
235239

236240
Deprecated Compiler Flags
237241
-------------------------

clang/docs/UsersManual.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,18 @@ are listed below.
21342134
new operator will always return a pointer that does not alias any
21352135
other pointer when the function returns.
21362136

2137+
.. option:: -fassume-nothrow-exception-dtor
2138+
2139+
Assume that an exception object' destructor will not throw, and generate
2140+
less code for catch handlers. A throw expression of a type with a
2141+
potentially-throwing destructor will lead to an error.
2142+
2143+
By default, Clang assumes that the exception object may have a throwing
2144+
destructor. For the Itanium C++ ABI, Clang generates a landing pad to
2145+
destroy local variables and call ``_Unwind_Resume`` for the code
2146+
``catch (...) { ... }``. This option tells Clang that an exception object's
2147+
destructor will not throw and code simplification is possible.
2148+
21372149
.. option:: -ftrap-function=[name]
21382150

21392151
Instruct code generator to emit a function call to the specified

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7977,6 +7977,8 @@ def err_return_in_constructor_handler : Error<
79777977
def warn_cdtor_function_try_handler_mem_expr : Warning<
79787978
"cannot refer to a non-static member from the handler of a "
79797979
"%select{constructor|destructor}0 function try block">, InGroup<Exceptions>;
7980+
def err_throw_object_throwing_dtor : Error<
7981+
"cannot throw object of type %0 with a potentially-throwing destructor">;
79807982

79817983
let CategoryName = "Lambda Issue" in {
79827984
def err_capture_more_than_once : Error<

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ ENUM_LANGOPT(ExceptionHandling, ExceptionHandlingKind, 3,
145145
ExceptionHandlingKind::None, "exception handling")
146146
LANGOPT(IgnoreExceptions , 1, 0, "ignore exceptions")
147147
LANGOPT(ExternCNoUnwind , 1, 0, "Assume extern C functions don't unwind")
148+
LANGOPT(AssumeNothrowExceptionDtor , 1, 0, "Assume exception object's destructor is nothrow")
148149
LANGOPT(TraditionalCPP , 1, 0, "traditional CPP emulation")
149150
LANGOPT(RTTI , 1, 1, "run-time type information")
150151
LANGOPT(RTTIData , 1, 1, "emit run-time type information data")

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,10 @@ def fignore_exceptions : Flag<["-"], "fignore-exceptions">, Group<f_Group>,
19971997
Visibility<[ClangOption, CC1Option]>,
19981998
HelpText<"Enable support for ignoring exception handling constructs">,
19991999
MarshallingInfoFlag<LangOpts<"IgnoreExceptions">>;
2000+
defm assume_nothrow_exception_dtor: BoolFOption<"assume-nothrow-exception-dtor",
2001+
LangOpts<"AssumeNothrowExceptionDtor">, DefaultFalse,
2002+
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Assume that exception objects' destructors are non-throwing">,
2003+
NegFlag<SetFalse>>;
20002004
def fexcess_precision_EQ : Joined<["-"], "fexcess-precision=">, Group<f_Group>,
20012005
Visibility<[ClangOption, CLOption]>,
20022006
HelpText<"Allows control over excess precision on targets where native "

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4508,7 +4508,9 @@ namespace {
45084508
}
45094509

45104510
/// Emits a call to __cxa_begin_catch and enters a cleanup to call
4511-
/// __cxa_end_catch.
4511+
/// __cxa_end_catch. If -fassume-nothrow-exception-dtor is specified, we assume
4512+
/// that the exception object's dtor is nothrow, therefore the __cxa_end_catch
4513+
/// call can be marked as nounwind even if EndMightThrow is true.
45124514
///
45134515
/// \param EndMightThrow - true if __cxa_end_catch might throw
45144516
static llvm::Value *CallBeginCatch(CodeGenFunction &CGF,
@@ -4517,7 +4519,9 @@ static llvm::Value *CallBeginCatch(CodeGenFunction &CGF,
45174519
llvm::CallInst *call =
45184520
CGF.EmitNounwindRuntimeCall(getBeginCatchFn(CGF.CGM), Exn);
45194521

4520-
CGF.EHStack.pushCleanup<CallEndCatch>(NormalAndEHCleanup, EndMightThrow);
4522+
CGF.EHStack.pushCleanup<CallEndCatch>(
4523+
NormalAndEHCleanup,
4524+
EndMightThrow && !CGF.CGM.getLangOpts().AssumeNothrowExceptionDtor);
45214525

45224526
return call;
45234527
}

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ static bool addExceptionArgs(const ArgList &Args, types::ID InputType,
385385
// So we do not set EH to false.
386386
Args.AddLastArg(CmdArgs, options::OPT_fignore_exceptions);
387387

388+
Args.addOptInFlag(CmdArgs, options::OPT_fassume_nothrow_exception_dtor,
389+
options::OPT_fno_assume_nothrow_exception_dtor);
390+
388391
if (EH)
389392
CmdArgs.push_back("-fexceptions");
390393
return EH;

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,16 @@ bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc,
11011101
<< (unsigned)ExnObjAlign.getQuantity();
11021102
}
11031103
}
1104+
if (!isPointer && getLangOpts().AssumeNothrowExceptionDtor) {
1105+
if (CXXDestructorDecl *Dtor = RD->getDestructor()) {
1106+
auto Ty = Dtor->getType();
1107+
if (auto *FT = Ty.getTypePtr()->getAs<FunctionProtoType>()) {
1108+
if (!isUnresolvedExceptionSpec(FT->getExceptionSpecType()) &&
1109+
!FT->isNothrow())
1110+
Diag(ThrowLoc, diag::err_throw_object_throwing_dtor) << RD;
1111+
}
1112+
}
1113+
}
11041114

11051115
return false;
11061116
}

clang/test/CodeGenCXX/eh.cpp

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.13.99 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=UNALIGNED %s
2-
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ALIGNED %s
1+
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.13.99 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,UNALIGNED,THROWEND %s
2+
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,ALIGNED,THROWEND %s
3+
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s -fassume-nothrow-exception-dtor -DNOTHROWEND | FileCheck --check-prefixes=CHECK,ALIGNED,NOTHROWEND %s
34

45
struct test1_D {
56
double d;
@@ -218,13 +219,16 @@ namespace test10 {
218219
} catch (B a) {
219220
// CHECK: call ptr @__cxa_begin_catch
220221
// CHECK-NEXT: call void @llvm.memcpy
221-
// CHECK-NEXT: invoke void @__cxa_end_catch()
222+
// THROWEND-NEXT: invoke void @__cxa_end_catch()
223+
// NOTHROWEND-NEXT: call void @__cxa_end_catch() [[NUW]]
222224
} catch (...) {
223225
// CHECK: call ptr @__cxa_begin_catch
224-
// CHECK-NEXT: invoke void @__cxa_end_catch()
226+
// THROWEND-NEXT: invoke void @__cxa_end_catch()
227+
// NOTHROWEND-NEXT: call void @__cxa_end_catch() [[NUW]]
225228
}
226229

227-
// CHECK: call void @_ZN6test101AD1Ev(
230+
// THROWEND: call void @_ZN6test101AD1Ev(
231+
// NOTHROWEND-NOT: call void @_ZN6test101AD1Ev(
228232
}
229233
}
230234

@@ -391,40 +395,42 @@ namespace test16 {
391395

392396
// CHECK-LABEL: define{{.*}} void @_ZN6test163barEv()
393397
void bar() {
394-
// CHECK: [[EXN_SAVE:%.*]] = alloca ptr
395-
// CHECK-NEXT: [[EXN_ACTIVE:%.*]] = alloca i1
396-
// CHECK-NEXT: [[TEMP:%.*]] = alloca [[A:%.*]],
397-
// CHECK-NEXT: [[EXNSLOT:%.*]] = alloca ptr
398-
// CHECK-NEXT: [[SELECTORSLOT:%.*]] = alloca i32
399-
// CHECK-NEXT: [[TEMP_ACTIVE:%.*]] = alloca i1
400-
398+
// THROWEND: [[EXN_SAVE:%.*]] = alloca ptr
399+
// THROWEND-NEXT: [[EXN_ACTIVE:%.*]] = alloca i1
400+
// THROWEND-NEXT: [[TEMP:%.*]] = alloca [[A:%.*]],
401+
// THROWEND-NEXT: [[EXNSLOT:%.*]] = alloca ptr
402+
// THROWEND-NEXT: [[SELECTORSLOT:%.*]] = alloca i32
403+
// THROWEND-NEXT: [[TEMP_ACTIVE:%.*]] = alloca i1
404+
405+
#ifndef NOTHROWEND
401406
cond() ? throw B(A()) : foo();
402-
403-
// CHECK-NEXT: [[COND:%.*]] = call noundef zeroext i1 @_ZN6test164condEv()
404-
// CHECK-NEXT: store i1 false, ptr [[EXN_ACTIVE]]
405-
// CHECK-NEXT: store i1 false, ptr [[TEMP_ACTIVE]]
406-
// CHECK-NEXT: br i1 [[COND]],
407-
408-
// CHECK: [[EXN:%.*]] = call ptr @__cxa_allocate_exception(i64 4)
409-
// CHECK-NEXT: store ptr [[EXN]], ptr [[EXN_SAVE]]
410-
// CHECK-NEXT: store i1 true, ptr [[EXN_ACTIVE]]
411-
// CHECK-NEXT: invoke void @_ZN6test161AC1Ev(ptr {{[^,]*}} [[TEMP]])
412-
// CHECK: store i1 true, ptr [[TEMP_ACTIVE]]
413-
// CHECK-NEXT: invoke void @_ZN6test161BC1ERKNS_1AE(ptr {{[^,]*}} [[EXN]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[TEMP]])
414-
// CHECK: store i1 false, ptr [[EXN_ACTIVE]]
415-
// CHECK-NEXT: invoke void @__cxa_throw(ptr [[EXN]],
416-
417-
// CHECK: invoke void @_ZN6test163fooEv()
418-
// CHECK: br label
419-
420-
// CHECK: invoke void @_ZN6test161AD1Ev(ptr {{[^,]*}} [[TEMP]])
421-
// CHECK: ret void
422-
423-
// CHECK: [[T0:%.*]] = load i1, ptr [[EXN_ACTIVE]]
424-
// CHECK-NEXT: br i1 [[T0]]
425-
// CHECK: [[T1:%.*]] = load ptr, ptr [[EXN_SAVE]]
426-
// CHECK-NEXT: call void @__cxa_free_exception(ptr [[T1]])
427-
// CHECK-NEXT: br label
407+
#endif
408+
409+
// THROWEND-NEXT: [[COND:%.*]] = call noundef zeroext i1 @_ZN6test164condEv()
410+
// THROWEND-NEXT: store i1 false, ptr [[EXN_ACTIVE]]
411+
// THROWEND-NEXT: store i1 false, ptr [[TEMP_ACTIVE]]
412+
// THROWEND-NEXT: br i1 [[COND]],
413+
414+
// THROWEND: [[EXN:%.*]] = call ptr @__cxa_allocate_exception(i64 4)
415+
// THROWEND-NEXT: store ptr [[EXN]], ptr [[EXN_SAVE]]
416+
// THROWEND-NEXT: store i1 true, ptr [[EXN_ACTIVE]]
417+
// THROWEND-NEXT: invoke void @_ZN6test161AC1Ev(ptr {{[^,]*}} [[TEMP]])
418+
// THROWEND: store i1 true, ptr [[TEMP_ACTIVE]]
419+
// THROWEND-NEXT: invoke void @_ZN6test161BC1ERKNS_1AE(ptr {{[^,]*}} [[EXN]], ptr noundef nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) [[TEMP]])
420+
// THROWEND: store i1 false, ptr [[EXN_ACTIVE]]
421+
// THROWEND-NEXT: invoke void @__cxa_throw(ptr [[EXN]],
422+
423+
// THROWEND: invoke void @_ZN6test163fooEv()
424+
// THROWEND: br label
425+
426+
// THROWEND: invoke void @_ZN6test161AD1Ev(ptr {{[^,]*}} [[TEMP]])
427+
// THROWEND: ret void
428+
429+
// THROWEND: [[T0:%.*]] = load i1, ptr [[EXN_ACTIVE]]
430+
// THROWEND-NEXT: br i1 [[T0]]
431+
// THROWEND: [[T1:%.*]] = load ptr, ptr [[EXN_SAVE]]
432+
// THROWEND-NEXT: call void @__cxa_free_exception(ptr [[T1]])
433+
// THROWEND-NEXT: br label
428434
}
429435
}
430436

clang/test/CodeGenCXX/exceptions.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++98 -o - -fcxx-exceptions -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK98 %s
2-
// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions | FileCheck -check-prefix=CHECK -check-prefix=CHECK11 %s
2+
// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions | FileCheck --check-prefixes=CHECK,CHECK11,THROWEND11 %s
3+
// RUN: %clang_cc1 -no-enable-noundef-analysis %s -triple=x86_64-linux-gnu -emit-llvm -std=c++11 -o - -fcxx-exceptions -fexceptions -fassume-nothrow-exception-dtor | FileCheck --check-prefixes=CHECK,CHECK11,NOTHROWEND11 %s
34

45
// CHECK: %[[STRUCT_TEST13_A:.*]] = type { i32, i32 }
56

@@ -479,11 +480,16 @@ namespace test10 {
479480

480481
// CHECK98: call void @__cxa_end_catch()
481482
// CHECK98-NEXT: br label
482-
// CHECK11: invoke void @__cxa_end_catch()
483-
// CHECK11-NEXT: to label
483+
// THROWEND11: invoke void @__cxa_end_catch()
484+
// THROWEND11-NEXT: to label %invoke.cont[[#]] unwind label %terminate.lpad
485+
// NOTHROWEND11: call void @__cxa_end_catch()
486+
// NOTHROWEND11-NEXT: br label %try.cont
484487

485488
// CHECK: invoke void @__cxa_rethrow()
486489
// CHECK: unreachable
490+
491+
// CHECK: terminate.lpad:
492+
// CHECK: call void @__clang_call_terminate(
487493
}
488494

489495
// Ensure that an exception in a constructor destroys

clang/test/CodeGenCoroutines/coro-cleanup.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Verify that coroutine promise and allocated memory are freed up on exception.
2-
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
2+
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s --check-prefixes=CHECK,THROWEND
3+
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -fassume-nothrow-exception-dtor -disable-llvm-passes | FileCheck %s --check-prefixes=CHECK,NOTHROWEND
34

45
namespace std {
56
template <typename... T> struct coroutine_traits;
@@ -49,7 +50,9 @@ void f() {
4950
// CHECK: [[DeallocPad]]:
5051
// CHECK-NEXT: landingpad
5152
// CHECK-NEXT: cleanup
52-
// CHECK: br label %[[Dealloc:.+]]
53+
// THROWEND: br label %[[Dealloc:.+]]
54+
// NOTHROWEND: icmp ne ptr %[[#]], null
55+
// NOTHROWEND-NEXT: br i1 %[[#]], label %[[Dealloc:.+]], label
5356

5457
Cleanup cleanup;
5558
may_throw();
@@ -68,22 +71,24 @@ void f() {
6871
// CHECK: [[Catch]]:
6972
// CHECK: call ptr @__cxa_begin_catch(
7073
// CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type19unhandled_exceptionEv(
71-
// CHECK: invoke void @__cxa_end_catch()
72-
// CHECK-NEXT: to label %[[Cont:.+]] unwind
74+
// THROWEND: invoke void @__cxa_end_catch()
75+
// THROWEND-NEXT: to label %[[Cont:.+]] unwind
76+
// NOTHROWEND: call void @__cxa_end_catch()
77+
// NOTHROWEND-NEXT: br label %[[Cont2:.+]]
7378

74-
// CHECK: [[Cont]]:
75-
// CHECK-NEXT: br label %[[Cont2:.+]]
76-
// CHECK: [[Cont2]]:
77-
// CHECK-NEXT: br label %[[Cleanup:.+]]
79+
// THROWEND: [[Cont]]:
80+
// THROWEND-NEXT: br label %[[Cont2:.+]]
81+
// CHECK: [[Cont2]]:
82+
// CHECK-NEXT: br label %[[Cleanup:.+]]
7883

7984
// CHECK: [[Cleanup]]:
8085
// CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_typeD1Ev(
8186
// CHECK: %[[Mem0:.+]] = call ptr @llvm.coro.free(
8287
// CHECK: call void @_ZdlPv(ptr noundef %[[Mem0]]
8388

8489
// CHECK: [[Dealloc]]:
85-
// CHECK: %[[Mem:.+]] = call ptr @llvm.coro.free(
86-
// CHECK: call void @_ZdlPv(ptr noundef %[[Mem]])
90+
// THROWEND: %[[Mem:.+]] = call ptr @llvm.coro.free(
91+
// THROWEND: call void @_ZdlPv(ptr noundef %[[Mem]])
8792

8893
co_return;
8994
}

clang/test/Driver/clang-exception-flags.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@
2727
// RUN: %clang -### -target x86_64-scei-ps4 %s 2>&1 | FileCheck %s -check-prefix=PS-OFF
2828
// RUN: %clang -### -target x86_64-sie-ps5 %s 2>&1 | FileCheck %s -check-prefix=PS-OFF
2929
// PS-OFF-NOT: "-cc1" {{.*}} "-f{{(cxx-)?}}exceptions"
30+
31+
// RUN: %clang -### -fexceptions -fno-assume-nothrow-exception-dtor -fassume-nothrow-exception-dtor %s 2>&1 | FileCheck %s --check-prefix=NOTHROW-DTOR
32+
// NOTHROW-DTOR: "-cc1"{{.*}} "-fcxx-exceptions" "-fassume-nothrow-exception-dtor"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only %s -fcxx-exceptions -fassume-nothrow-exception-dtor -verify
2+
3+
namespace test1 {
4+
template <typename T> struct A { A(); ~A(); };
5+
struct B { ~B() noexcept(false); };
6+
struct B1 : B {};
7+
struct B2 { B b; };
8+
struct C { virtual void f(); } c;
9+
struct MoveOnly { MoveOnly(); MoveOnly(MoveOnly&&); };
10+
void run() {
11+
throw A<int>();
12+
throw B(); // expected-error{{cannot throw object of type 'B' with a potentially-throwing destructor}}
13+
throw new B;
14+
throw B1(); // expected-error{{cannot throw object of type 'B1' with a potentially-throwing destructor}}
15+
B2 b2;
16+
throw b2; // expected-error{{cannot throw object of type 'B2' with a potentially-throwing destructor}}
17+
throw c;
18+
MoveOnly m;
19+
throw m;
20+
}
21+
}

0 commit comments

Comments
 (0)