Bridging Swift Error Handling Model to C++ (GSoC)

Hello @Alex_L!

My name is Roberto Rosmaninho, and I’m currently in my last year as an undergrad Computer Science student at the Universidade Federal de Minas Gerais in Brazil. I'm looking forward to participating in this year's GSoC event with Swift. I'm very interested in the project concerning bringing the Swift Error Handling Model to C++.

I have a strong background in C++ and compiler design, especially in code generation using the LLVM C++ API. Although I don't have much experience with Swift, I'm very interested in learning the programming language and the structure of its compiler before the beginning of the development phase of the GSoC program to have as much knowledge as possible to develop this project. To that end, I would greatly appreciate any tips on how to become familiar with the language and the compiler, especially with what I'll need for this project.

This will be my second year on the GSoC program, as I participated last year in the LLVM Back-end for the Tensor Algebra Compiler project for the Python Software Foundation. Also, I previously contributed to the D Programming Language in their Symmetry Autumn of Code, a similar open-source program to GSoC, on the project MLIR Back-end for the D Programming Language. I am now very much looking forward to contributing to the Swift Community leading up to, during, and after GSoC!

As a C++ programmer and an Apple enthusiast I'm loving reading about Swift and C++ Interoperability on the forum. Therefore, I would love to know more about this project and its details to draft a formal proposal to debate it with you and the community!

Best regards,
Roberto Rosmaninho

12 Likes

Hey @Robertorosmaninho! Thanks for voicing out your interest in this.

I can provide you with some additional insight into what this project entails. I am working on teaching the Swift compiler how to generate a C++ header that is able to generate C++ function thunks that represent Swift functions and methods, so that they can be called from C++. For example, for the following Swift function:

// module Functions
func passVoidReturnVoid() { print("hello world") }

the following C++ interface will be generated:

SWIFT_EXTERN void $s9Functions014passVoidReturnC0yyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // passVoidReturnVoid()

namespace Functions {

inline void passVoidReturnVoid(void) noexcept { // C++ thunk with C++ interface
  return $s9Functions014passVoidReturnC0yyF();
}

}

This approach would need to be extend in this GSoC to generate C++ thunks for Swift functions that can throw. This project would need to support two different strategies:

  • The generated C++ function would need to throw a C++ exception that wraps around a Swift Error value, when Swift throws an error.
  • The generated C++ function would need to return a Swift Error value inside another class that stores either the original return value or the error. That C++ class could resemble the proposed std::expected class. This will provide error handling for clients that don't use C++ exceptions.

The Swift Error itself is an existential Swift value. It would need to be represented using another C++ class, which will have to be generated by the Swift compiler. This work will also have to be done as part of this GSoC project.

Swift's Error values are sometimes cast / matched to another error type to determine the precise type of the error in Swift. C++ will not have initial support for casting the Swift Error value, and this GSoC project does not have to support that, as that's something that can be done later.

The next step that GSoC recommends is to start working on writing a project proposal based on this idea. Feel free to post your proposal draft on the forums, so that we can provide some feedback for you to help your proposal. In addition to that, I would also recommend reading through getting started docs for Swift, to get a sense of how you can setup a working environment for you to work on the Swift compiler:

Let me know if you have any other questions!

Cheers,
Alex

3 Likes

Hello @Alex_L and everyone else!
I was building Swift with Xcode (but the same happened with Ninja too) for the first time on the new macOS 12.3 and I found out that this new version of macOS doesn't have the Python2.7 natively in /usr/bin, so when I try to get this version of Python for the -DPython2_EXECUTABLE to increment the swift_cmake_options on utils/build-script-impl we get the following error:

xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk macosx -find python2.7 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "python2.7", not a developer tool or in PATH
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk macosx -find python2.7 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "python2.7", not a developer tool or in PATH

I'd like to know if this python version is used for something that python3 can't be used, so I can report the bug or create a PR to solve this :slight_smile: !

@Robertorosmaninho At the moment there is no limitation to use python3, in fact I think all scripts would make python 3 default at some point (There is a PR for that in progress Sign in to GitHub · GitHub)

Also, not sure if it would work help you could try to define an alias for python like alias python=python3 in an attempt to trick build scripts to invoke python3. That's something one can try to see if helps but not sure if would fix the issue still didn't update =]

Hey @LucianoPAlmeida thanks for your quick reply!

Unfortunately, I can't see the content of this PR :confused:

The error appears due to utils/build-script-impl tries to find the python2.7, a quick fix that works was just erase this line -DPython2_EXECUTABLE="$(xcrun -f python2.7)", I don't know if this PR already do that, but if not I strongly suggest that it does! :)

For some reason the link didn't resolve correctly, but here it is =]

The PR is basically adjusting python env to python3 in all scripts for what I could see

1 Like

Hello @Alex_L and everyone!

This week, I studied the code and the documentation about the state of the art on C++ Interop.

So far, I was able to reproduce your simple "Hello World" example and write a function that should throw an error but doesn't. After doing that, I searched the code on Swift that is writing the C++ header to better understand the process for translation from Swift to C++.

The idea this week was to explore the first part of the project:

  • Extend the C++ interface generator for a Swift module to emit C++ interfaces for Swift functions that throw; and
  • Generate a C++ class that represents Swift’s Error type.

I believe that I can deliver this first part by the first deadline with some tests. What do you think?
From my understanding, the modifications of the project will be done on the directory PrintAsClang mainly at ModuleContentsWriter.cpp, DeclAndTypePrinter.cpp and PrintAsClang.cpp.

I'm still studying the second part:

  • Implement a C++ exception class that wraps around the Error type.
  • C++ class that resembles the proposed std::expected class to provide error handling for clients that don’t use C++ exceptions

Where the first task seems to be easier than the second.
My question here is how can I split those tasks in the best way to write a feasible and precise schedule as possible? I already wrote a scratch of it, but, in my opinion, it was too general, and I want to write the best proposal for this project that I'm very excited to start working on!

Also, I would like to know if still is possible to get into the C++ Interop Group? I think that I can learn (and contribute soon) following close this discussion and implementation.

Finally, do you prefer that I share the draft of my proposal here or send it to you, @Alex_L, in an email?

Best Regards,
Roberto Rosmaninho

1 Like

Hey, great to hear back from you!

So far, I was able to reproduce your simple "Hello World" example and write a function that should throw an error but doesn't.

Great!

I believe that I can deliver this first part by the first deadline with some tests. What do you think?
From my understanding, the modifications of the project will be done on the directory PrintAsClang mainly at ModuleContentsWriter.cpp , DeclAndTypePrinter.cpp and PrintAsClang.cpp .

Yes, the bulk of the reverse interop code is indeed in that subdirectory. I think your idea for the deliverable here makes sense. I think that should be reasonable as a deliverable by the first deadline as well.

I'm still studying the second part:

  • Implement a C++ exception class that wraps around the Error type.
  • C++ class that resembles the proposed std::expected class to provide error handling for clients that > don’t use C++ exceptions

Where the first task seems to be easier than the second.
My question here is how can I split those tasks in the best way to write a feasible and precise schedule > as possible? I already wrote a scratch of it, but, in my opinion, it was too general, and I want to write the > best proposal for this project that I'm very excited to start working on!

You can split up the work on implementation and testing and writing the documentation for the classes to better align with your proposed schedule. For example, you could say that you will work an initial implementation (including tests) of this class that holds Error in week 1. The initial implementation will just hold the Error value and you will provide an API for constructing it and extracting the error value from. Then in week 2 you can say you will work on integrating this initial class implementation together with the function generator, so now functions that throw will be able to actually construct this class and throw the C++ exception, and that you will add tests that show that the user can catch it. Then in week 3, you can say that you will fix any outstanding bugs and that you will add documentation for this class and how to use it from C++.

Also, I would like to know if still is possible to get into the C++ Interop Group? I think that I can learn (and contribute soon) following close this discussion and implementation.

Yes, for sure. We can add you to the group seeing that you're interested in contributing. I will reach out to you.

Finally, do you prefer that I share the draft of my proposal here or send it to you, @Alex_L, in an email?

Whatever works best for you. If you don't mind sharing it publicly you can post it here and I will review it on this thread, otherwise send me a personal message on the forums so that I can review it for you.

1 Like

Hey @Alex_L,
I've finally understood the std::expected class, and that's awesome! I'm also a Haskell programmer, so the similarity of this proposal with the Either monad surprised me, and I'm looking forward to implementing it on this project!!

However, something that I didn't understand is this use case:

The idea here is just to test if the function did not return a value using if (result.has_value()) or if (result), and then the user will deal with it whatever they want or did you thought in something different?

A second question: should I have to implement the whole std::expected proposition or just some subset?

Finally, just to clarify, do you expect two C++ Classes that wrap up Swift's Error: one simple C++ class and another one that extends the std::exception? Or just this last one?

Best regards,
Roberto Rosmaninho

3 Likes

The idea here is just to test if the function did not return a value using if (result.has_value()) or if (result) , and then the user will deal with it whatever they want or did you thought in something different?

Yes, that's correct. They will be forced to check whether the result has value or an error in it, if they opt in into the no-exception mode.

A second question: should I have to implement the whole std::expected proposition or just some subset?

A subset should be sufficient. We don't need the entire interface to be implemented right away. We would want to have support for creating an error or result expected value, and then checking if it has an error or a result when it's passed to the user.

Finally, just to clarify, do you expect two C++ Classes that wrap up Swift's Error : one simple C++ class and another one that extends the std::exception ? Or just this last one?

We need two classes. The class that wraps Swift's Error is separate from the std::exception class, as you need to be able to operate on any Error from C++ outside of an exception value. The class that represents Swift's any Error will most likely be generated from the compiler fwiw as it's just an existential type (i.e. a polymorphic protocol with a boxed value of type we don't know about statically). You don't need to specify the details of how it will be generated as well in the proposal, you can state that it will be generated by the compiler as it's an existential type.

1 Like

Hello @Alex_L,

Thanks for answering my questions! It was very clarifying, and with that help, I could finally finish my proposal.

Therefore, I'm now publishing the proposal here. So, you and everyone from the community read it and hopefully give some feedback here and/or comments there to improve it. Not only to be accepted on this year's Google Summer of Code program but also to help me implement it soon! :slight_smile:

Best regards,
Roberto Rosmaninho

1 Like

Hey @Robertorosmaninho, thanks for posting your proposal! I left some initial feedback on the main part of your proposal. I will try to provide some additional feedback on the schedule as well tomorrow.

2 Likes

Thank you very much, @Alex_L !!

2 Likes

Hey @Alex_L,

I just answered your comments and changed the document following them. Could you please give it another look?

Also, could you elaborate on the difference between "extend the std::exception" and "a new exception class subclassing exception"? From my understanding, these things are equivalent, but maybe I'm seeing this from the wrong perspective!

Finally, could you provide me an example of how we can build/use a template type, like swift::Exception<swift::Error> e to specify which error value the user can catch? I'm more or less used to doing something like that with different subclasses of std::exception, I think you mean something like it, but I would like to be sure of it!

Thank you very much for your patience, availability, and guidance on this proposal!

Best regards,
Roberto Rosmaninho

2 Likes

Great, thanks! The proposal looks better now, so thanks for making the suggested changes :slight_smile:
Please don't forget to submit it before the deadline (which is coming up tomorrow on the 19th of April at 6pm UTC).

Also, could you elaborate on the difference between "extend the std::exception" and "a new exception class subclassing exception"? From my understanding, these things are equivalent, but maybe I'm seeing this from the wrong perspective!

"extend" is a bit of an overloaded term as "extending" the class means something different in Swift. Even though we're talking about C++, it's good to be precise here and mention that we want to subclass std::exception to create a new class that stores a swift::Error value, rather than trying to shoehorn the error into the existing std::exception class.

Finally, could you provide me an example of how we can build/use a template type, like swift::Exception<swift::Error> e to specify which error value the user can catch? I'm more or less used to doing something like that with different subclasses of std::exception , I think you mean something like it, but I would like to be sure of it!

Actually please ignore this suggestion! I realized it's not going to work, as we can't construct a swift::Exception class template as we don't know the possible error types that Swift can throw. So, we need to keep a non-templated swift::Exception that just stores swift::Error value, and the user will need to cast it to the appropriate error type if they want to cast it to a specific Swift error type.

3 Likes

Hey @Alex_L,

Thanks for the explanation; it's much clearer now! About the suggestion, I kept the swift::exception as it was, as requested :slight_smile: !

I just submitted the proposal!! I'm very confident and looking forward to starting working on this project!

Best regards,
Roberto Rosmaninho

2 Likes