Skip to content

Commit 2c83ff7

Browse files
authored
Merge pull request #61823 from Robertorosmaninho/interop/ExpectedClassForErrorHandling
[Interop][SwiftToCxx] Introduces swift::Expected
2 parents 515715b + 6d0dd0e commit 2c83ff7

File tree

6 files changed

+406
-19
lines changed

6 files changed

+406
-19
lines changed

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,7 @@ EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx(
799799
os << "#ifndef SWIFT_CXX_INTEROP_HIDE_STL_OVERLAY\n";
800800
os << "#include <string>\n";
801801
os << "#endif\n";
802+
os << "#include <new>\n";
802803
// Embed an overlay for the standard library.
803804
ClangSyntaxPrinter(moduleOS).printIncludeForShimHeader(
804805
"_SwiftStdlibCxxOverlay.h");

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,8 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
688688
ClangRepresentation::representable;
689689

690690
// Print out the return type.
691+
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)
692+
os << "Swift::ThrowingResult<";
691693
if (kind == FunctionSignatureKind::CFunctionProto) {
692694
// First, verify that the C++ return type is representable.
693695
{
@@ -740,7 +742,8 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
740742
.isUnsupported())
741743
return resultingRepresentation;
742744
}
743-
745+
if (FD->hasThrows() && outputLang == OutputLanguageMode::Cxx)
746+
os << ">";
744747
os << ' ';
745748
if (const auto *typeDecl = modifiers.qualifierContext)
746749
ClangSyntaxPrinter(os).printNominalTypeQualifier(
@@ -1241,14 +1244,43 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
12411244
// Create the condition and the statement to throw an exception.
12421245
if (hasThrows) {
12431246
os << " if (opaqueError != nullptr)\n";
1247+
os << "#ifdef __cpp_exceptions\n";
12441248
os << " throw (Swift::Error(opaqueError));\n";
1245-
}
1249+
os << "#else\n";
1250+
if (resultTy->isVoid()) {
1251+
os << " return SWIFT_RETURN_THUNK(void, Swift::Error(opaqueError));\n";
1252+
os << "#endif\n";
1253+
} else {
1254+
auto directResultType = signature.getDirectResultType();
1255+
printDirectReturnOrParamCType(
1256+
*directResultType, resultTy, moduleContext, os, cPrologueOS,
1257+
typeMapping, interopContext, [&]() {
1258+
os << " return SWIFT_RETURN_THUNK(";
1259+
OptionalTypeKind retKind;
1260+
Type objTy;
1261+
std::tie(objTy, retKind) =
1262+
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);
12461263

1247-
// Return the function result value if it doesn't throw.
1248-
if (!resultTy->isVoid() && hasThrows) {
1249-
os << "\n";
1250-
os << "return returnValue;\n";
1264+
auto s = printClangFunctionReturnType(objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
1265+
OutputLanguageMode::Cxx);
1266+
os << ", Swift::Error(opaqueError));\n";
1267+
os << "#endif\n";
1268+
1269+
// Return the function result value if it doesn't throw.
1270+
if (!resultTy->isVoid() && hasThrows) {
1271+
os << "\n";
1272+
os << " return SWIFT_RETURN_THUNK(";
1273+
printClangFunctionReturnType(
1274+
objTy, retKind, const_cast<ModuleDecl *>(moduleContext),
1275+
OutputLanguageMode::Cxx);
1276+
os << ", returnValue);\n";
1277+
}
1278+
1279+
assert(!s.isUnsupported());
1280+
});
1281+
}
12511282
}
1283+
12521284
}
12531285

12541286
static StringRef getConstructorName(const AbstractFunctionDecl *FD) {

lib/PrintAsClang/_SwiftStdlibCxxOverlay.h

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ inline const void *_Nullable getErrorMetadata() {
166166
return ptr2;
167167
}
168168

169+
#ifndef SWIFT_CXX_INTEROP_HIDE_SWIFT_ERROR
170+
169171
class Error {
170172
public:
171173
Error() {}
@@ -213,4 +215,196 @@ class Error {
213215
void *_Nonnull opaqueValue = nullptr;
214216
};
215217

218+
namespace _impl {
219+
220+
constexpr inline std::size_t max(std::size_t a, std::size_t b) {
221+
return a > b ? a : b;
222+
}
223+
224+
} // namespace _impl
225+
226+
/// The Expected class has either an error or an value.
227+
template<class T>
228+
class Expected {
229+
public:
230+
231+
/// Default
232+
constexpr Expected() noexcept {
233+
new (&buffer) Error();
234+
has_val = false;
235+
}
236+
237+
constexpr Expected(const Swift::Error& error_val) noexcept {
238+
new (&buffer) Error(error_val);
239+
has_val = false;
240+
}
241+
242+
constexpr Expected(const T &val) noexcept {
243+
new (&buffer) T(val);
244+
has_val = true;
245+
}
246+
247+
/// Copy
248+
constexpr Expected(Expected const& other) noexcept {
249+
if (other.has_value())
250+
new (&buffer) T(other.value());
251+
else
252+
new (&buffer) Error(other.error());
253+
254+
has_val = other.has_value();
255+
}
256+
257+
/// Move
258+
// FIXME: Implement move semantics when move Swift values is possible
259+
constexpr Expected(Expected&&) noexcept { abort(); }
260+
261+
~Expected() noexcept {
262+
if (has_value())
263+
reinterpret_cast<const T *>(buffer)->~T();
264+
else
265+
reinterpret_cast<Swift::Error *>(buffer)->~Error();
266+
}
267+
268+
/// assignment
269+
constexpr auto operator=(Expected&& other) noexcept = delete;
270+
constexpr auto operator=(Expected&) noexcept = delete;
271+
272+
/// For accessing T's members
273+
constexpr T const *_Nonnull operator->() const noexcept {
274+
if (!has_value())
275+
abort();
276+
return reinterpret_cast<const T *>(buffer);
277+
}
278+
279+
constexpr T *_Nonnull operator->() noexcept {
280+
if (!has_value())
281+
abort();
282+
return reinterpret_cast<T *>(buffer);
283+
}
284+
285+
/// Getting reference to T
286+
constexpr T const &operator*() const & noexcept {
287+
if (!has_value())
288+
abort();
289+
return reinterpret_cast<const T &>(buffer);
290+
}
291+
292+
constexpr T &operator*() & noexcept {
293+
if (!has_value())
294+
abort();
295+
return reinterpret_cast<T &>(buffer);
296+
}
297+
298+
constexpr explicit operator bool() const noexcept { return has_value(); }
299+
300+
// Get value, if not exists abort
301+
constexpr T const& value() const& {
302+
if (!has_value())
303+
abort();
304+
return *reinterpret_cast<const T *>(buffer);
305+
}
306+
307+
constexpr T& value() & {
308+
if (!has_value())
309+
abort();
310+
return *reinterpret_cast<T *>(buffer);
311+
}
312+
313+
// Get error
314+
constexpr Swift::Error const& error() const& {
315+
if (has_value())
316+
abort();
317+
return reinterpret_cast<const Swift::Error&>(buffer);
318+
}
319+
320+
constexpr Swift::Error& error() & {
321+
if (has_value())
322+
abort();
323+
return reinterpret_cast<Swift::Error&>(buffer);
324+
}
325+
326+
constexpr bool has_value() const noexcept { return has_val; }
327+
328+
private:
329+
alignas(_impl::max(alignof(T), alignof(Swift::Error))) char buffer[_impl::max(sizeof(T), sizeof(Swift::Error))];
330+
bool has_val;
331+
};
332+
333+
template<>
334+
class Expected<void> {
335+
public:
336+
/// Default
337+
Expected() noexcept {
338+
new (&buffer) Error();
339+
has_val = false;
340+
}
341+
342+
Expected(const Swift::Error& error_val) noexcept {
343+
new (&buffer) Error(error_val);
344+
has_val = false;
345+
}
346+
347+
348+
/// Copy
349+
Expected(Expected const& other) noexcept {
350+
if (other.has_value())
351+
abort();
352+
else
353+
new (&buffer) Error(other.error());
354+
355+
has_val = other.has_value();
356+
}
357+
358+
/// Move
359+
// FIXME: Implement move semantics when move swift values is possible
360+
[[noreturn]] Expected(Expected&&) noexcept { abort(); }
361+
362+
~Expected() noexcept {
363+
reinterpret_cast<Swift::Error *>(buffer)->~Error();
364+
}
365+
366+
/// assignment
367+
constexpr auto operator=(Expected&& other) noexcept = delete;
368+
constexpr auto operator=(Expected&) noexcept = delete;
369+
370+
371+
constexpr explicit operator bool() const noexcept { return has_value(); }
372+
373+
// Get error
374+
constexpr Swift::Error const& error() const& {
375+
if (has_value())
376+
abort();
377+
return reinterpret_cast<const Swift::Error&>(buffer);
378+
}
379+
380+
constexpr Swift::Error& error() & {
381+
if (has_value())
382+
abort();
383+
return reinterpret_cast<Swift::Error&>(buffer);
384+
}
385+
386+
constexpr bool has_value() const noexcept { return has_val; }
387+
private:
388+
alignas(alignof(Swift::Error)) char buffer[sizeof(Swift::Error)];
389+
bool has_val;
390+
};
391+
392+
#ifdef __cpp_exceptions
393+
394+
template<class T>
395+
using ThrowingResult = T;
396+
397+
#define SWIFT_RETURN_THUNK(T, v) v
398+
399+
#else
400+
401+
template<class T>
402+
using ThrowingResult = Swift::Expected<T>;
403+
404+
#define SWIFT_RETURN_THUNK(T, v) Swift::Expected<T>(v)
405+
406+
#endif
407+
408+
#endif // SWIFT_CXX_INTEROP_HIDE_SWIFT_ERROR
409+
216410
#endif

0 commit comments

Comments
 (0)