bool IsLateTemplateParsed : 1;
bool IsConstexpr : 1;
+ /// \brief Indicates if the function uses __try.
+ bool UsesSEHTry : 1;
+
/// \brief Indicates if the function was a definition but its body was
/// skipped.
unsigned HasSkippedBody : 1;
HasWrittenPrototype(true), IsDeleted(false), IsTrivial(false),
IsDefaulted(false), IsExplicitlyDefaulted(false),
HasImplicitReturnZero(false), IsLateTemplateParsed(false),
- IsConstexpr(isConstexprSpecified), HasSkippedBody(false),
- EndRangeLoc(NameInfo.getEndLoc()),
+ IsConstexpr(isConstexprSpecified), UsesSEHTry(false),
+ HasSkippedBody(false), EndRangeLoc(NameInfo.getEndLoc()),
TemplateOrSpecialization(),
DNLoc(NameInfo.getInfo()) {}
bool isConstexpr() const { return IsConstexpr; }
void setConstexpr(bool IC) { IsConstexpr = IC; }
+ /// Whether this is a (C++11) constexpr function or constexpr constructor.
+ bool usesSEHTry() const { return UsesSEHTry; }
+ void setUsesSEHTry(bool UST) { UsesSEHTry = UST; }
+
/// \brief Whether this function has been deleted.
///
/// A function that is "deleted" (via the C++0x "= delete" syntax)
"cannot use '%0' with exceptions disabled">;
def err_objc_exceptions_disabled : Error<
"cannot use '%0' with Objective-C exceptions disabled">;
+def err_seh_try_outside_functions : Error<
+ "cannot use SEH '__try' in blocks, captured regions, or Obj-C method decls">;
def err_mixing_cxx_try_seh_try : Error<
"cannot use C++ 'try' in the same function as SEH '__try'">;
def note_conflicting_try_here : Note<
// This function must have prototype void(void*).
const char *CatchallRethrowFn;
- static const EHPersonality &get(CodeGenModule &CGM);
+ static const EHPersonality &get(CodeGenModule &CGM,
+ const FunctionDecl *FD);
+ static const EHPersonality &get(CodeGenFunction &CGF) {
+ return get(CGF.CGM, dyn_cast_or_null<FunctionDecl>(CGF.CurCodeDecl));
+ }
+
static const EHPersonality GNU_C;
static const EHPersonality GNU_C_SJLJ;
static const EHPersonality GNU_C_SEH;
static const EHPersonality GNU_CPlusPlus_SEH;
static const EHPersonality MSVC_except_handler;
static const EHPersonality MSVC_C_specific_handler;
+ static const EHPersonality MSVC_CxxFrameHandler3;
};
}
EHPersonality::MSVC_except_handler = { "_except_handler3", nullptr };
const EHPersonality
EHPersonality::MSVC_C_specific_handler = { "__C_specific_handler", nullptr };
+const EHPersonality
+EHPersonality::MSVC_CxxFrameHandler3 = { "__CxxFrameHandler3", nullptr };
/// On Win64, use libgcc's SEH personality function. We fall back to dwarf on
/// other platforms, unless the user asked for SjLj exceptions.
llvm_unreachable("bad runtime kind");
}
-static const EHPersonality &getCPersonalityMSVC(const llvm::Triple &T,
- const LangOptions &L) {
- if (L.SjLjExceptions)
- return EHPersonality::GNU_C_SJLJ;
-
+static const EHPersonality &getSEHPersonalityMSVC(const llvm::Triple &T) {
if (T.getArch() == llvm::Triple::x86)
return EHPersonality::MSVC_except_handler;
return EHPersonality::MSVC_C_specific_handler;
}
-static const EHPersonality &getCXXPersonalityMSVC(const llvm::Triple &T,
- const LangOptions &L) {
- if (L.SjLjExceptions)
- return EHPersonality::GNU_CPlusPlus_SJLJ;
- // FIXME: Implement C++ exceptions.
- return getCPersonalityMSVC(T, L);
-}
-
-const EHPersonality &EHPersonality::get(CodeGenModule &CGM) {
+const EHPersonality &EHPersonality::get(CodeGenModule &CGM,
+ const FunctionDecl *FD) {
const llvm::Triple &T = CGM.getTarget().getTriple();
const LangOptions &L = CGM.getLangOpts();
+
// Try to pick a personality function that is compatible with MSVC if we're
// not compiling Obj-C. Obj-C users better have an Obj-C runtime that supports
// the GCC-style personality function.
if (T.isWindowsMSVCEnvironment() && !L.ObjC1) {
- if (L.CPlusPlus)
- return getCXXPersonalityMSVC(T, L);
+ if (L.SjLjExceptions)
+ return EHPersonality::GNU_CPlusPlus_SJLJ;
+ else if (FD && FD->usesSEHTry())
+ return getSEHPersonalityMSVC(T);
else
- return getCPersonalityMSVC(T, L);
+ return EHPersonality::MSVC_CxxFrameHandler3;
}
if (L.CPlusPlus && L.ObjC1)
if (!LangOpts.ObjCRuntime.isNeXTFamily())
return;
- const EHPersonality &ObjCXX = EHPersonality::get(*this);
+ const EHPersonality &ObjCXX = EHPersonality::get(*this, /*FD=*/nullptr);
const EHPersonality &CXX =
getCXXPersonality(getTarget().getTriple(), LangOpts);
if (&ObjCXX == &CXX)
assert(EHStack.requiresLandingPad());
assert(!EHStack.empty());
- if (!CGM.getLangOpts().Exceptions)
- return nullptr;
+ // If exceptions are disabled, there are usually no landingpads. However, when
+ // SEH is enabled, functions using SEH still get landingpads.
+ const LangOptions &LO = CGM.getLangOpts();
+ if (!LO.Exceptions) {
+ if (!LO.Borland && !LO.MicrosoftExt)
+ return nullptr;
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(CurCodeDecl);
+ if (!FD || !FD->usesSEHTry())
+ return nullptr;
+ }
// Check the innermost scope for a cached landing pad. If this is
// a non-EH cleanup, we'll check enclosing scopes in EmitLandingPad.
CGBuilderTy::InsertPoint savedIP = Builder.saveAndClearIP();
auto DL = ApplyDebugLocation::CreateDefaultArtificial(*this, CurEHLocation);
- const EHPersonality &personality = EHPersonality::get(CGM);
+ const EHPersonality &personality = EHPersonality::get(*this);
// Create and configure the landing pad.
llvm::BasicBlock *lpad = createBasicBlock("lpad");
Builder.SetInsertPoint(TerminateLandingPad);
// Tell the backend that this is a landing pad.
- const EHPersonality &Personality = EHPersonality::get(CGM);
+ const EHPersonality &Personality = EHPersonality::get(*this);
llvm::LandingPadInst *LPadInst =
Builder.CreateLandingPad(llvm::StructType::get(Int8PtrTy, Int32Ty, nullptr),
getOpaquePersonalityFn(CGM, Personality), 0);
EHResumeBlock = createBasicBlock("eh.resume");
Builder.SetInsertPoint(EHResumeBlock);
- const EHPersonality &Personality = EHPersonality::get(CGM);
+ const EHPersonality &Personality = EHPersonality::get(*this);
// This can always be a call because we necessarily didn't find
// anything on the EH stack which needs our help.
const Driver &D = getToolChain().getDriver();
EHFlags EH = parseClangCLEHFlags(D, Args);
// FIXME: Do something with NoExceptC.
- if (EH.Synch || EH.Asynch)
+ if (EH.Synch || EH.Asynch) {
CmdArgs.push_back("-fcxx-exceptions");
- // Always add -fexceptions to allow SEH __try.
- CmdArgs.push_back("-fexceptions");
+ CmdArgs.push_back("-fexceptions");
+ }
// /EP should expand to -E -P.
if (Args.hasArg(options::OPT__SLASH_EP)) {
if (getCurScope() && getCurScope()->isOpenMPSimdDirectiveScope())
Diag(TryLoc, diag::err_omp_simd_region_cannot_use_stmt) << "try";
+ sema::FunctionScopeInfo *FSI = getCurFunction();
+
// C++ try is incompatible with SEH __try.
- if (!getLangOpts().Borland && getCurFunction()->FirstSEHTryLoc.isValid()) {
+ if (!getLangOpts().Borland && FSI->FirstSEHTryLoc.isValid()) {
Diag(TryLoc, diag::err_mixing_cxx_try_seh_try);
- Diag(getCurFunction()->FirstSEHTryLoc, diag::note_conflicting_try_here)
- << "'__try'";
+ Diag(FSI->FirstSEHTryLoc, diag::note_conflicting_try_here) << "'__try'";
}
const unsigned NumHandlers = Handlers.size();
}
}
- getCurFunction()->setHasCXXTry(TryLoc);
+ FSI->setHasCXXTry(TryLoc);
// FIXME: We should detect handlers that cannot catch anything because an
// earlier handler catches a superclass. Need to find a method that is not
Stmt *TryBlock, Stmt *Handler) {
assert(TryBlock && Handler);
+ sema::FunctionScopeInfo *FSI = getCurFunction();
+
// SEH __try is incompatible with C++ try. Borland appears to support this,
// however.
- if (!getLangOpts().Borland && getCurFunction()->FirstCXXTryLoc.isValid()) {
- Diag(TryLoc, diag::err_mixing_cxx_try_seh_try);
- Diag(getCurFunction()->FirstCXXTryLoc, diag::note_conflicting_try_here)
- << "'try'";
+ if (!getLangOpts().Borland) {
+ if (FSI->FirstCXXTryLoc.isValid()) {
+ Diag(TryLoc, diag::err_mixing_cxx_try_seh_try);
+ Diag(FSI->FirstCXXTryLoc, diag::note_conflicting_try_here) << "'try'";
+ }
}
- getCurFunction()->setHasSEHTry(TryLoc);
+ FSI->setHasSEHTry(TryLoc);
+
+ // Reject __try in Obj-C methods, blocks, and captured decls, since we don't
+ // track if they use SEH.
+ DeclContext *DC = CurContext;
+ while (DC && !DC->isFunctionOrMethod())
+ DC = DC->getParent();
+ FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(DC);
+ if (FD)
+ FD->setUsesSEHTry(true);
+ else
+ Diag(TryLoc, diag::err_seh_try_outside_functions);
return SEHTryStmt::Create(Context, IsCXXTry, TryLoc, TryBlock, Handler);
}
-// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fexceptions -fms-extensions -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s
void abort(void) __attribute__((noreturn));
void might_crash(void);
-// RUN: not %clang_cc1 -triple x86_64-pc-win32 -fexceptions -fms-extensions -emit-llvm -o - %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - %s 2>&1 | FileCheck %s
// This is a codegen test because we only emit the diagnostic when we start
// generating code.
-// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fexceptions -fms-extensions -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple x86_64-pc-win32 -fms-extensions -emit-llvm -o - | FileCheck %s
// FIXME: Perform this outlining automatically CodeGen.
void try_body(int numerator, int denominator, int *myres) {
--- /dev/null
+// RUN: %clang_cc1 -std=c++11 -fblocks -fms-extensions %s -triple=x86_64-windows-msvc -emit-llvm \
+// RUN: -o - -mconstructor-aliases -fcxx-exceptions -fexceptions | \
+// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CXXEH
+// RUN: %clang_cc1 -std=c++11 -fblocks -fms-extensions %s -triple=x86_64-windows-msvc -emit-llvm \
+// RUN: -o - -mconstructor-aliases | \
+// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=NOCXX
+
+extern "C" void might_throw();
+
+struct HasCleanup {
+ HasCleanup();
+ ~HasCleanup();
+ int padding;
+};
+
+extern "C" void use_cxx() {
+ HasCleanup x;
+ might_throw();
+}
+
+// Make sure we use __CxxFrameHandler3 for C++ EH.
+
+// CXXEH-LABEL: define void @use_cxx()
+// CXXEH: call %struct.HasCleanup* @"\01??0HasCleanup@@QEAA@XZ"(%struct.HasCleanup* %{{.*}})
+// CXXEH: invoke void @might_throw()
+// CXXEH: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
+//
+// CXXEH: [[cont]]
+// CXXEH: call void @"\01??1HasCleanup@@QEAA@XZ"(%struct.HasCleanup* %{{.*}})
+// CXXEH: ret void
+//
+// CXXEH: [[lpad]]
+// CXXEH: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+// CXXEH-NEXT: cleanup
+// CXXEH: call void @"\01??1HasCleanup@@QEAA@XZ"(%struct.HasCleanup* %{{.*}})
+// CXXEH: br label %[[resume:[^ ]*]]
+//
+// CXXEH: [[resume]]
+// CXXEH: resume
+
+// NOCXX-LABEL: define void @use_cxx()
+// NOCXX-NOT: invoke
+// NOCXX: call %struct.HasCleanup* @"\01??0HasCleanup@@QEAA@XZ"(%struct.HasCleanup* %{{.*}})
+// NOCXX-NOT: invoke
+// NOCXX: call void @might_throw()
+// NOCXX-NOT: invoke
+// NOCXX: call void @"\01??1HasCleanup@@QEAA@XZ"(%struct.HasCleanup* %{{.*}})
+// NOCXX-NOT: invoke
+// NOCXX: ret void
+
+extern "C" void use_seh() {
+ __try {
+ might_throw();
+ } __except(1) {
+ }
+}
+
+// Make sure we use __C_specific_handler for SEH.
+
+// CHECK-LABEL: define void @use_seh()
+// CHECK: invoke void @might_throw()
+// CHECK: to label %[[cont:[^ ]*]] unwind label %[[lpad:[^ ]*]]
+//
+// CHECK: [[cont]]
+// CHECK: br label %[[ret:[^ ]*]]
+//
+// CHECK: [[lpad]]
+// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+// CHECK-NEXT: catch i8*
+//
+// CHECK: br label %[[ret]]
+//
+// CHECK: [[ret]]
+// CHECK: ret void
+
+void use_seh_in_lambda() {
+ ([]() {
+ __try {
+ might_throw();
+ } __except(1) {
+ }
+ })();
+ HasCleanup x;
+ might_throw();
+}
+
+// CXXEH-LABEL: define void @"\01?use_seh_in_lambda@@YAXXZ"()
+// CXXEH: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+
+// NOCXX-LABEL: define void @"\01?use_seh_in_lambda@@YAXXZ"()
+// NOCXX-NOT: invoke
+// NOCXX: ret void
+
+// CHECK-LABEL: define internal void @"\01??R<lambda_0>@?use_seh_in_lambda@@YAXXZ@QEBAXXZ"(%class.anon* %this)
+// CHECK: landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
// RUN: %clang_cl /c /EHs-c- -### -- %s 2>&1 | FileCheck -check-prefix=EHs_c_ %s
// EHs_c_-NOT: "-fcxx-exceptions"
-// EHs_c_: "-fexceptions"
+// EHs_c_-NOT: "-fexceptions"
// RUN: %clang_cl /c /EHs- /EHc- -### -- %s 2>&1 | FileCheck -check-prefix=EHs_EHc_ %s
// EHs_EHc_-NOT: "-fcxx-exceptions"
-// EHs_EHc_: "-fexceptions"
+// EHs_EHc_-NOT: "-fexceptions"
// RUN: %clang_cl /c /EHs- /EHs -### -- %s 2>&1 | FileCheck -check-prefix=EHs_EHs %s
// EHs_EHs: "-fcxx-exceptions"
-// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions -fsyntax-only -fexceptions -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++03 -fblocks -triple x86_64-windows-msvc -fms-extensions -fsyntax-only -fexceptions -fcxx-exceptions -verify %s
+// RUN: %clang_cc1 -std=c++11 -fblocks -triple x86_64-windows-msvc -fms-extensions -fsyntax-only -fexceptions -fcxx-exceptions -verify %s
// Basic usage should work.
int safe_div(int n, int d) {
bad_builtin_scope_template<might_crash>();
}
+#if __cplusplus < 201103L
// FIXME: Diagnose this case. For now we produce undef in codegen.
template <typename T, T FN()>
T func_template() {
func_template<void *, __exception_info>();
func_template<unsigned long, __exception_code>();
}
+#endif
void use_seh_after_cxx() {
try { // expected-note {{conflicting 'try' here}}
} catch (int) {
}
}
+
+#if __cplusplus >= 201103L
+void use_seh_in_lambda() {
+ ([]() {
+ __try {
+ might_crash();
+ } __except(1) {
+ }
+ })();
+ try {
+ might_crash();
+ } catch (int) {
+ }
+}
+#endif
+
+void use_seh_in_block() {
+ void (^b)() = ^{
+ __try { // expected-error {{cannot use SEH '__try' in blocks, captured regions, or Obj-C method decls}}
+ might_crash();
+ } __except(1) {
+ }
+ };
+ try {
+ b();
+ } catch (int) {
+ }
+}
+
+void (^use_seh_in_global_block)() = ^{
+ __try { // expected-error {{cannot use SEH '__try' in blocks, captured regions, or Obj-C method decls}}
+ might_crash();
+ } __except(1) {
+ }
+};
+
+void (^use_cxx_in_global_block)() = ^{
+ try {
+ might_crash();
+ } catch(int) {
+ }
+};