[PrintAsClang] Fix thunks for throwing Never funcs

Extend the previous commit’s support for functions that return Never to also properly generate code for *throwing* Never functions. This is a little subtle because:

• At the SWIFT_CALL level, throwing Never functions are *not* noreturn
• At the thunk level, throwing Never functions are noreturn *only* if you’re using exceptions; if you’re using swift::Expected, they should throw
• In either case, the compiler cannot statically prove that thunks are noreturn except on the error path, so we need to add an abort() call on the success path
This commit is contained in:
Becca Royal-Gordon
2024-03-18 11:32:35 -07:00
parent 80f38be3b8
commit a02698ba06
7 changed files with 54 additions and 5 deletions

View File

@@ -1410,7 +1410,10 @@ private:
/// Print C or C++ trailing attributes for a function declaration.
void printFunctionClangAttributes(FuncDecl *FD, AnyFunctionType *funcTy) {
if (funcTy->getResult()->isUninhabited()) {
os << " SWIFT_NORETURN";
if (funcTy->isThrowing())
os << " SWIFT_NORETURN_EXCEPT_ERRORS";
else
os << " SWIFT_NORETURN";
} else if (!funcTy->getResult()->isVoid() &&
!FD->getAttrs().hasAttribute<DiscardableResultAttr>()) {
os << " SWIFT_WARN_UNUSED_RESULT";
@@ -1489,12 +1492,15 @@ private:
// Swift functions can't throw exceptions, we can only
// throw them from C++ when emitting C++ inline thunks for the Swift
// functions.
if (!funcTy->isThrowing())
if (!funcTy->isThrowing()) {
os << " SWIFT_NOEXCEPT";
// Lowered Never-returning functions *are* considered to return when they
// throw, so only use SWIFT_NORETURN on non-throwing functions.
if (funcTy->getResult()->isUninhabited())
os << " SWIFT_NORETURN";
}
if (!funcABI.useCCallingConvention())
os << " SWIFT_CALL";
if (funcTy->getResult()->isUninhabited())
os << " SWIFT_NORETURN";
printAvailability(FD);
os << ';';
if (funcABI.useMangledSymbolName()) {

View File

@@ -1444,6 +1444,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
if (resultTy->isVoid()) {
os << " return swift::Expected<void>(swift::Error(opaqueError));\n";
os << "#endif\n";
if (FD->getInterfaceType()->castTo<FunctionType>()->getResult()->isUninhabited())
os << " abort();\n";
} else {
auto directResultType = signature.getDirectResultType();
printDirectReturnOrParamCType(

View File

@@ -413,12 +413,14 @@ template<class T>
using ThrowingResult = T;
#define SWIFT_RETURN_THUNK(T, v) v
#define SWIFT_NORETURN_EXCEPT_ERRORS SWIFT_NORETURN
#else
template <class T> using ThrowingResult = swift::Expected<T>;
#define SWIFT_RETURN_THUNK(T, v) swift::Expected<T>(v)
#define SWIFT_NORETURN_EXCEPT_ERRORS
#endif

View File

@@ -89,6 +89,16 @@ int main() {
valueError.getMessage();
}
auto expectedResult2 = Functions::throwFunctionWithNeverReturn();
if (!expectedResult2.has_value()) {
auto error = expectedResult2.error();
auto optionalError = error.as<Functions::NaiveErrors>();
assert(optionalError.isSome());
auto valueError = optionalError.get();
assert(valueError == Functions::NaiveErrors::returnError);
valueError.getMessage();
}
// Test get T's Value (const)
const auto valueExp = testIntValue;
if (valueExp.value() == 42)
@@ -116,6 +126,8 @@ int main() {
// CHECK-NEXT: returnError
// CHECK-NEXT: passThrowFunctionWithPossibleReturn
// CHECK-NEXT: returnError
// CHECK-NEXT: passThrowFunctionWithNeverReturn
// CHECK-NEXT: returnError
// CHECK-NEXT: Test get T's Value (const)
// CHECK-NEXT: Test get T's Value
// CHECK-NEXT: testIntValue has a value

View File

@@ -46,6 +46,11 @@ int main() {
} catch (swift::Error& e) {
printf("Exception\n");
}
try {
Functions::throwFunctionWithNeverReturn();
} catch (swift::Error& e) {
printf("Exception\n");
}
try {
Functions::testDestroyedError();
} catch(const swift::Error &e) { }
@@ -58,4 +63,6 @@ int main() {
// CHECK-NEXT: throwError
// CHECK-NEXT: passThrowFunctionWithReturn
// CHECK-NEXT: Exception
// CHECK-NEXT: passThrowFunctionWithNeverReturn
// CHECK-NEXT: Exception
// CHECK-NEXT: Test destroyed

View File

@@ -14,6 +14,7 @@
// CHECK: SWIFT_EXTERN void $s9Functions18emptyThrowFunctionyyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // emptyThrowFunction()
// CHECK: SWIFT_EXTERN void $s9Functions18testDestroyedErroryyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // testDestroyedError()
// CHECK: SWIFT_EXTERN void $s9Functions13throwFunctionyyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // throwFunction()
// CHECK: SWIFT_EXTERN void $s9Functions28throwFunctionWithNeverReturns0E0OyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // throwFunctionWithNeverReturn()
// CHECK: SWIFT_EXTERN ptrdiff_t $s9Functions31throwFunctionWithPossibleReturnyS2iKF(ptrdiff_t a, SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // throwFunctionWithPossibleReturn(_:)
// CHECK: SWIFT_EXTERN ptrdiff_t $s9Functions23throwFunctionWithReturnSiyKF(SWIFT_CONTEXT void * _Nonnull _ctx, SWIFT_ERROR_RESULT void * _Nullable * _Nullable _error) SWIFT_CALL; // throwFunctionWithReturn()
@@ -89,6 +90,25 @@ public func throwFunction() throws {
// CHECK: #endif
// CHECK: }
@_expose(Cxx)
public func throwFunctionWithNeverReturn() throws -> Never {
print("passThrowFunctionWithNeverReturn")
throw NaiveErrors.returnError
}
// CHECK: SWIFT_INLINE_THUNK swift::ThrowingResult<void> throwFunctionWithNeverReturn() SWIFT_SYMBOL("s:9Functions28throwFunctionWithNeverReturns0E0OyKF") SWIFT_NORETURN_EXCEPT_ERRORS {
// CHECK-NEXT: void* opaqueError = nullptr;
// CHECK-NEXT: void* _ctx = nullptr;
// CHECK-NEXT: _impl::$s9Functions28throwFunctionWithNeverReturns0E0OyKF(_ctx, &opaqueError);
// CHECK-NEXT: if (opaqueError != nullptr)
// CHECK-NEXT: #ifdef __cpp_exceptions
// CHECK-NEXT: throw (swift::Error(opaqueError));
// CHECK-NEXT: #else
// CHECK-NEXT: return swift::Expected<void>(swift::Error(opaqueError));
// CHECK-NEXT: #endif
// CHECK-NEXT: abort();
// CHECK-NEXT: }
@_expose(Cxx)
public func throwFunctionWithPossibleReturn(_ a: Int) throws -> Int {
print("passThrowFunctionWithPossibleReturn")

View File

@@ -12,7 +12,7 @@
// CHECK: SWIFT_EXTERN int $s9Functions016passTwoIntReturnD01x1ys5Int32VAF_AFtF(int x, int y) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnInt(x:y:)
// CHECK: SWIFT_EXTERN int $s9Functions016passTwoIntReturnD10NoArgLabelys5Int32VAD_ADtF(int, int) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnIntNoArgLabel(_:_:)
// CHECK: SWIFT_EXTERN int $s9Functions016passTwoIntReturnD19NoArgLabelParamNameys5Int32VAD_ADtF(int x2, int y2) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnIntNoArgLabelParamName(_:_:)
// CHECK: SWIFT_EXTERN void $s9Functions19passVoidReturnNevers0E0OyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_NORETURN; // passVoidReturnNever()
// CHECK: SWIFT_EXTERN void $s9Functions19passVoidReturnNevers0E0OyF(void) SWIFT_NOEXCEPT SWIFT_NORETURN SWIFT_CALL; // passVoidReturnNever()
// CHECK: SWIFT_EXTERN void $s9Functions014passVoidReturnC0yyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // passVoidReturnVoid()
// CHECK: }