Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[libc++] Fix std::make_exception_ptr interaction with ObjC #135386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 22 commits into
base: main
Choose a base branch
Loading
from

Conversation

itrofimow
Copy link
Contributor

@itrofimow itrofimow commented Apr 11, 2025

Clang treats throwing/catching ObjC types differently from C++ types, and omitting the throw in std::make_exception_ptr breaks ObjC invariants about how types are thrown/caught.

Fixes #135089

@itrofimow itrofimow changed the title fix std::make_exception_ptr interaction with ObjC [libc++] fix std::make_exception_ptr interaction with ObjC Apr 14, 2025
@itrofimow itrofimow marked this pull request as ready for review April 14, 2025 19:04
@itrofimow itrofimow requested a review from a team as a code owner April 14, 2025 19:04
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Apr 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 14, 2025

@llvm/pr-subscribers-libcxx

Author: None (itrofimow)

Changes

Clang treats throwing/catching ObjC types differently from C++ types, and omitting the throw in std::make_exception_ptr breaks ObjC invariants about how types are thrown/caught


Full diff: https://github.com/llvm/llvm-project/pull/135386.diff

1 Files Affected:

  • (modified) libcxx/include/__exception/exception_ptr.h (+2-1)
diff --git a/libcxx/include/__exception/exception_ptr.h b/libcxx/include/__exception/exception_ptr.h
index dac5b00b57fe3..167aa4d9b367a 100644
--- a/libcxx/include/__exception/exception_ptr.h
+++ b/libcxx/include/__exception/exception_ptr.h
@@ -92,7 +92,8 @@ class _LIBCPP_EXPORTED_FROM_ABI exception_ptr {
 template <class _Ep>
 _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep __e) _NOEXCEPT {
 #  if _LIBCPP_HAS_EXCEPTIONS
-#    if _LIBCPP_AVAILABILITY_HAS_INIT_PRIMARY_EXCEPTION && __cplusplus >= 201103L
+  // Clang treats throwing ObjC types differently, and we have to preserve original throw-ing behavior
+#    if !defined(__OBJC__) && _LIBCPP_AVAILABILITY_HAS_INIT_PRIMARY_EXCEPTION && __cplusplus >= 201103L
   using _Ep2 = __decay_t<_Ep>;
 
   void* __ex = __cxxabiv1::__cxa_allocate_exception(sizeof(_Ep));

@jyknight
Copy link
Member

It's not great to condition only on __OBJC__ preprocessor define, since that'll create a potential ODR issue for std::make_exception_ptr on a C++ type, when you're mixing a TU built with C++ and another TU built with ObjC++ in the same program (which, everyone is).

If you switch to the throw impl only for ObjC object-pointers, that stops being a problem, as an ObjC object pointer type cannot exist in a non-ObjC++ TU.

As a refinement to my suggestion on the issue, I think std::is_pointer_v<_Ep> && std::is_convertible_v<_Ep, id> would be better (so as to not accidentally trigger on a C++ value with a user-defined conversion operator id()).

@jyknight
Copy link
Member

Are you still planning to update this patch?

@itrofimow
Copy link
Contributor Author

Hi! Yes, sorry, just got back to office

I was thinking, wouldn't just std::is_pointer_v<_Ep> condition be better it terms of readability?
After all, this is just an optimization, and given that people very seldom create exception pointers from pointers, it shouldn't be that much of a performance hit, and the less odd conditions we have here the better

@jyknight
Copy link
Member

You mean without the OBJC ifdef? I think that would be OK. But IMO it's not actually an improvement, because the condition is no longer clearly connected with the actual reason for the existence of the alternate codepath.

@itrofimow
Copy link
Contributor Author

I don't think CI failure has to do with these changes, @jyknight could you please have a look?

using _Ep2 = __decay_t<_Ep>;

void* __ex = __cxxabiv1::__cxa_allocate_exception(sizeof(_Ep));
# if _LIBCPP_AVAILABILITY_HAS_INIT_PRIMARY_EXCEPTION && __cplusplus >= 201703L
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should really be _LIBCPP_STD_VER >= 17. Also, why did this change from C++11 to C++17?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is about language-level features rather than libc++ features: c++11 or higher was required for the lambda to compile; c++17 or higher is now required for if constexpr

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both are available as extensions in older versions. No need to put this behind a guard.

@itrofimow itrofimow requested a review from philnik777 May 20, 2025 10:26
Copy link
Member

@jyknight jyknight left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change looks fine.

But is there any way to add a test-case for this? (It's not clear to me there is since it depends on the non-llvm-project objc runtime, but it'd be great if there was.)

libcxx/include/__exception/exception_ptr.h Outdated Show resolved Hide resolved
@rsesek
Copy link

rsesek commented Jun 5, 2025

Change looks fine.

But is there any way to add a test-case for this? (It's not clear to me there is since it depends on the non-llvm-project objc runtime, but it'd be great if there was.)

There's some prior art in func.blocks.arc.pass.mm for depending on the Blocks runtime in a test. Not sure if it's also okay to depend on the host libobjc similarly.

@itrofimow
Copy link
Contributor Author

itrofimow commented Jun 10, 2025

I'll see what I can do about the test for this.
I don't think it's actually necessary (but would be nice to have) to depend on objc runtime: maybe just a objc-codegen test is somewhat enough

@ldionne ldionne changed the title [libc++] fix std::make_exception_ptr interaction with ObjC [libc++] Fix std::make_exception_ptr interaction with ObjC Jul 2, 2025
@ldionne
Copy link
Member

ldionne commented Jul 2, 2025

@itrofimow @jyknight Thanks for the PR. I updated the PR with a test and I refactored the code as a drive-by so it would be easier to understand -- after adding yet another if in there, the function was getting pretty hard to follow. Can you please have a look at the PR? I'd like to get it merged whenever possible.

@ldionne
Copy link
Member

ldionne commented Jul 2, 2025

We might be able to make it into LLVM 20.1.9 if we end up having such a release.

//
// [1]:
// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Exceptions/Articles/Exceptions64Bit.html
if constexpr (is_pointer<_Ep>::value) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, if there was a way to query Clang whether _Ep is an Objective-C pointer type, that would match exactly the condition they use in

if (ThrowType->isObjCObjectPointerType()) {
to decide whether to generate an Objective-C throw or a normal C++ throw. Do you think that is something we should be able to query directly from Clang?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had suggested std::is_pointer_v<_Ep> && std::is_convertible_v<_Ep, id> earlier if we want to avoid C++ pointers.

Copy link
Member

@ldionne ldionne Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that id is not a known identifier in C++, so we have to guard this with an #ifdef checking whether we're compiling in Objective-C mode, and I think we had ODR issues with doing that?

Naively adding the check right now, I get:

exception_ptr.h:141:71: error: use of undeclared identifier 'id'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right; it needs an ifdef. That shouldn't cause issues -- I think -- because the template arg cannot be an ObjC type and thus convertible to id if it's not an objc file. The reason not to do it was just itrofimow's feeling it was too complicated. It may well be, I don't have strong feelings here. So probably best to just leave it the way you have it now.

libcxx/include/__exception/exception_ptr.h Show resolved Hide resolved
@ldionne
Copy link
Member

ldionne commented Jul 4, 2025

Gentle ping @itrofimow @jyknight WDYT about the current PR? It's passing CI and I'd like to get it merged whenever possible!

@itrofimow
Copy link
Contributor Author

Hi @ldionne! Thank you for taking care of this PR, I've got completely consumed by work and absolutely failed to clean up my own mess in time.

Personally, I don't think narrowing the "could it be ObjC type" condition any further via compiler intrinsics is worth it: for me (as someone who just reads libcxx source code from time to time, not a maintainer) the mere presence of ObjC-related code/comments in libcxx seems confusing enough, and I'd say that std::make_exception_ptr<T*> in C++ is a use-case too niche to further complicate code here, especially via something directly ObjC-related.

Nice refactoring by you, and I'm happy to see CI finally working.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] [libc++abi] Regression: std::make_exception_ptr breaks catching ObjC objects on rethrow
6 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.